Skip to content

Commit d2b0abe

Browse files
authored
chore: move LCS from hackerspace (#71)
1 parent 0f0e381 commit d2b0abe

File tree

22 files changed

+706
-0
lines changed

22 files changed

+706
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Gno Live Coding Session #1
2+
3+
#### Topic: Minimal Constant Product GRC20 AMM in Gno
4+
#### Date: Friday, Aug 16th 2024
5+
#### Presenter: [@leohhhn](https://github.com/leohhhn)
6+
7+
This live coding session was the first Gno Live coding session.
8+
It included building a basic [`GRC20`](https://gno.land/p/demo/grc/grc20) DEX
9+
in Gno with the [constant product AMM](https://medium.com/@solidity101/the-constant-product-automated-market-maker-amm-294976dbb657)
10+
algorithm. The base implementation only handles two GRC20 tokens:
11+
[`wugnot`](https://gno.land/r/demo/wugnot) & [`foo20`](https://gno.land/r/demo/foo20).
12+
13+
## Following up
14+
15+
We encourage anyone who wishes to build upon the example given in the coding
16+
session to do so. Please make a PR adding your code to the
17+
[`followup-work/`](./followup-work) folder, and ping the presenter.
18+
19+
> Due to technical issues, the video for this session is missing. :/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package lcsdex
2+
3+
import (
4+
"std"
5+
"strconv"
6+
7+
"gno.land/p/demo/ufmt"
8+
"gno.land/p/demo/users"
9+
"gno.land/r/demo/foo20"
10+
"gno.land/r/demo/wugnot"
11+
)
12+
13+
// Possible improvements to this code
14+
// Handle multiple liquidity pools to allow for any grc20 swap
15+
// Implement rewards for liquidity providers
16+
// Improve rendering & error messages
17+
// Add tests
18+
19+
type LastTrade struct {
20+
addr std.Address
21+
amtWugnot uint64
22+
amtFoo20 uint64
23+
typ string // "wff" || "ffw"
24+
}
25+
26+
var (
27+
wugnotReserve uint64
28+
foo20Reserve uint64
29+
dexAddr = std.CurrentRealm().Addr() // Realm{pkgPath, address} // std.Address
30+
lt *LastTrade
31+
)
32+
33+
// constant product amm -> k = x * y
34+
35+
// amm -> LP (reserves of tokens) > who provides reseves
36+
// Please, use grc20.Approve before calling this function
37+
func AddLiquidity(fooAmt, wugnotAmt uint64) string {
38+
// add both wugnot & foo20 reserves
39+
// provide equal amounts
40+
// user provides amounts
41+
// approves beforehand
42+
if fooAmt == 0 || wugnotAmt == 0 {
43+
panic("you need to provide both tokens to the liquidity pool")
44+
}
45+
46+
if fooAmt != wugnotAmt {
47+
panic("provided amounts need to be the same")
48+
}
49+
50+
caller := std.PrevRealm().Addr() // std.Address // type Address string
51+
// todo: implement fee system
52+
53+
foo20.TransferFrom(users.AddressOrName(caller.String()), users.AddressOrName(dexAddr.String()), fooAmt)
54+
wugnot.TransferFrom(users.AddressOrName(caller.String()), users.AddressOrName(dexAddr.String()), wugnotAmt)
55+
56+
wugnotReserve += wugnotAmt
57+
foo20Reserve += fooAmt
58+
59+
return ufmt.Sprintf("successfully deposited %d wugnot & %d foo20", wugnotAmt, fooAmt)
60+
}
61+
62+
func SwapWugnotForFoo20(amtInWugnot, amtOutFoo20 uint64) string {
63+
if amtInWugnot == 0 || amtOutFoo20 == 0 {
64+
panic("you need to provide both in and out amounts")
65+
}
66+
67+
// k = x * y
68+
k := wugnotReserve * foo20Reserve
69+
70+
newWugnotReserve := wugnotReserve + amtInWugnot
71+
newFoo20Reserve := k / newWugnotReserve
72+
73+
amtOut := foo20Reserve - newFoo20Reserve
74+
75+
if amtOut < amtOutFoo20 {
76+
msg := ufmt.Sprintf("not enough liquidity. you can only get %d foo20 for %d wugnot", amtOut, amtInWugnot)
77+
panic(msg)
78+
}
79+
80+
caller := std.PrevRealm().Addr()
81+
wugnot.TransferFrom(users.AddressOrName(caller.String()), users.AddressOrName(dexAddr.String()), amtInWugnot)
82+
foo20.Transfer(users.AddressOrName(caller.String()), amtOutFoo20)
83+
84+
wugnotReserve = newWugnotReserve
85+
foo20Reserve = newFoo20Reserve
86+
87+
lt = &LastTrade{
88+
addr: caller,
89+
amtWugnot: amtInWugnot,
90+
amtFoo20: amtOutFoo20,
91+
typ: "wff", // wugnot for foo20
92+
}
93+
94+
return ufmt.Sprintf("successfully swapped %d wugnot for %d foo20", amtInWugnot, amtOutFoo20)
95+
}
96+
97+
func SwapFoo20ForWugnot(amtInFoo20, amountOutWugnot uint64) string {
98+
if amtInFoo20 == 0 || amountOutWugnot == 0 {
99+
panic("you need to provide both in and out amounts")
100+
}
101+
102+
// k = x * y
103+
k := wugnotReserve * foo20Reserve
104+
105+
newFoo20Reserve := foo20Reserve + amtInFoo20
106+
newWugnotReserve := k / newFoo20Reserve
107+
108+
amtOut := wugnotReserve - newWugnotReserve
109+
110+
if amtOut < amountOutWugnot {
111+
msg := ufmt.Sprintf("not enough liquidity. you can only get %d wugnot for %d foo20", amtOut, amtInFoo20)
112+
panic(msg)
113+
}
114+
115+
caller := std.PrevRealm().Addr()
116+
foo20.TransferFrom(users.AddressOrName(caller.String()), users.AddressOrName(dexAddr.String()), amtInFoo20)
117+
wugnot.Transfer(users.AddressOrName(caller.String()), amountOutWugnot)
118+
119+
wugnotReserve = newWugnotReserve
120+
foo20Reserve = newFoo20Reserve
121+
122+
lt = &LastTrade{
123+
addr: caller,
124+
amtWugnot: amountOutWugnot,
125+
amtFoo20: amtInFoo20,
126+
typ: "ffw", // foo20 for wugnot
127+
}
128+
129+
return ufmt.Sprintf("successfully swapped %d foo20 for %d wugnot", amtInFoo20, amountOutWugnot)
130+
}
131+
132+
func Render(_ string) string {
133+
output := "# GRC20 Simple DEX\n\n"
134+
135+
output += ufmt.Sprintf("DEX Address: %s\n\n", dexAddr.String())
136+
output += ufmt.Sprintf("Liquidity: %d wugnot, %d foo20\n\n", wugnotReserve, foo20Reserve)
137+
output += ufmt.Sprintf("1 wugnot = %s foo20\n\n", manualFloat(wugnotReserve, foo20Reserve))
138+
output += ufmt.Sprintf("1 foo20 = %s wugnot\n\n", manualFloat(foo20Reserve, wugnotReserve))
139+
140+
if lt != nil {
141+
output += "### Last Swap\n\n"
142+
143+
if lt.typ == "wff" {
144+
output += ufmt.Sprintf("%s traded %d wugnot for %d foo20", lt.addr.String(), lt.amtWugnot, lt.amtFoo20)
145+
} else {
146+
output += ufmt.Sprintf("%s traded %d foo20 for %d wugnot", lt.addr.String(), lt.amtFoo20, lt.amtWugnot)
147+
}
148+
}
149+
150+
return output
151+
}
152+
153+
func manualFloat(a, b uint64) string {
154+
quotient := a / b
155+
remainder := a % b
156+
157+
quotientStr := strconv.FormatUint(quotient, 10)
158+
scaledRemainder := (remainder * 1000000) / b
159+
remainderStr := strconv.FormatUint(scaledRemainder, 10)
160+
161+
return quotientStr + "." + remainderStr
162+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module gno.land/r/lcs/dex
2+
3+
require (
4+
gno.land/p/demo/ufmt v0.0.0-latest
5+
gno.land/p/demo/users v0.0.0-latest
6+
gno.land/r/demo/foo20 v0.0.0-latest
7+
gno.land/r/demo/wugnot v0.0.0-latest
8+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Followup work
2+
3+
To add your code for this GnoLCS, create a directory named after your GitHub username
4+
under this folder.
5+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Gno Live Coding Session #2
2+
3+
#### Topic: A gno.land home realm
4+
#### Date: Friday, Aug 30th 2024
5+
#### Presenter: [@leohhhn](https://github.com/leohhhn)
6+
7+
This live coding session showed attendees how to build their own home realm for
8+
gno.land.
9+
10+
The home realm for gno.land is meant to be similar to what a GitHub profile page
11+
is for GitHub. It's personal, and it includes information about the user.
12+
13+
In this specific example, we create a home realm that adds an image, text about
14+
the user, and also showcases dynamic imports of `r/demo/art` realms. All of this
15+
is then combined into an output to showcase how the `Render()` function works
16+
in Gno.
17+
18+
## Following up
19+
20+
We encourage anyone who wishes to build upon the example given in the coding
21+
session to do so. Please make a PR adding your code to the
22+
[`followup-work/`](./followup-work) folder, and ping the presenter.
23+
24+
## Video
25+
26+
Video recording can be found [here](https://www.youtube.com/watch?v=ZI0ZGDMbj-U).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Package config is simply Leon's configuration file.
2+
// It can be used externally to dynamically give Leon the rights to call something;
3+
// Say, an admin variable can be set to r/leon/config.Address(). Then, Leon can
4+
// update his address and still retain admin priveleges.
5+
package config
6+
7+
import (
8+
"errors"
9+
"std"
10+
)
11+
12+
var (
13+
main std.Address
14+
backup std.Address
15+
)
16+
17+
func init() {
18+
main = "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5" // registered to @leon
19+
}
20+
21+
func Address() std.Address {
22+
return main
23+
}
24+
25+
func Backup() std.Address {
26+
return backup
27+
}
28+
29+
func SetAddress(newAddr std.Address) {
30+
AssertAuthorized()
31+
if !newAddr.IsValid() {
32+
panic("config: invalid address")
33+
}
34+
35+
main = newAddr
36+
}
37+
38+
func SetBackup(newAddr std.Address) {
39+
AssertAuthorized()
40+
if !newAddr.IsValid() {
41+
panic("config: invalid address")
42+
}
43+
44+
backup = newAddr
45+
}
46+
47+
func CheckAuthorized() error {
48+
caller := std.PrevRealm().Addr()
49+
50+
if caller != main || caller != backup {
51+
return errors.New("config: unauthorized")
52+
}
53+
54+
return nil
55+
}
56+
57+
func AssertAuthorized() {
58+
caller := std.PrevRealm().Addr()
59+
60+
if caller != main || caller != backup {
61+
panic("config: unauthorized")
62+
}
63+
}
64+
65+
// Possible improvements
66+
// Add errors, add a non-panicking function for Authorized
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module gno.land/r/leon/config
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module gno.land/r/leon/home
2+
3+
require (
4+
gno.land/p/demo/ufmt v0.0.0-latest
5+
gno.land/r/demo/art/gnoface v0.0.0-latest
6+
gno.land/r/leon/config v0.0.0-latest
7+
)

0 commit comments

Comments
 (0)