Gno: Examples and Comparisons
Nebular, 13 Jul 2024, Brussels
Manfred Touron
VP Eng., Gno.land
Manfred Touron
VP Eng., Gno.land
package main import "fmt" func main() { fmt.Println("Hello, Nebular! It's Manfred.") }
package hello func Hello() string { return "hello world" }
package counter var Counter int func Inc() { Counter += 1 } func Render(path string) string { return "My Super Counter: " + Counter }
package counter import "std" var ( Counter int LastCaller std.Address ) func Inc() int { return addToCounter(amount) } func Add(amount int) int { return addToCounter(amount) } func addToCounter(amount int) int { Counter += amount LastCaller = std.GetOrigCaller() return Counter }
package guest import "std" var messages avl.Tree // std.Address -> string (message) func AddMessage(message string) { caller := std.GetOrigCaller() if _, ok := messages.Get(caller); ok { panic("this user already post a message") } messages.Set(caller, message) // add message to our messages list } func Render(path string) string { var view string for _, message := range messages { view = view + "\n" + message // add message to the render } return view }
package std // import "std" func AssertOriginCall() func Emit(typ string, attrs ...string) func GetChainID() string func GetHeight() int64 func IsOriginCall() bool type Address string func DerivePkgAddr(pkgPath string) Address func GetOrigCaller() Address type Banker interface{ ... } func GetBanker(bt BankerType) Banker type Coin struct{ ... } func NewCoin(denom string, amount int64) Coin type Coins []Coin func GetOrigSend() Coins func NewCoins(coins ...Coin) Coins type Realm struct{ ... } func CurrentRealm() Realm func PrevRealm() Realm
The GnoVM enables
seamless interoperability of
untrusted user programs
written in a good language.
10package alice var x int func GetX() int { return x } func SetX(n int) { x = n }
package bob import "alice" func IncrAlice() { x := alice.GetX() alice.SetX(x + 1) }
package users var ( addressToUsers avl.Tree // address -> user usersToAddress avl.Tree // user -> address ) func Register(name string) { caller := std.GetOrigCaller() if usersToAddress.Has(name) || addressToUsers.Has(caller) { panic("address/name already registered") } usersToAddress.Set(name, std.GetOrignCaller()) addressToUsers.Set(std.GetOrignCaller(), name) }
package users var admin = "g1xxxxx..." var invites avl.Tree // std.Address -> true func AdminSendInvite(address std.Address) { if caller := std.GetOrigCaller(); caller != admin { panic("unauthorized") } invites.Set(address, true) } func Register() { caller := std.GetOrigCaller() if !invites.Has(caller) { panic("caller hasn't been invited") } // ... register caller invites.Remove(address) }
package tamagochi // Tamagotchi structure type Tamagotchi struct { hunger int happiness int health int } func (t *Tamagotchi) Feed() { t.updateStats() if t.dead() { return } t.hunger = bound(t.hunger-10, 0, 100) } func (t *Tamagotchi) Heal() { /* ... */ } func (t *Tamagotchi) Play() { /* ... */ }
package tamagochi var tam Tamagochi // Interaction function ... func Render(path string) string { switch { case tam.Health() == 0: return "😵" // dead face case tam.Health() < 30: return "😷" // sick face case tam.Happiness() < 30: return "😢" // sad face case tam.Hunger() > 70: return "😫" // hungry face default: return "😃" // happy face } }
Implement a GRC20 token standard similar to ERC20 in Solidity.
// gno.land/p/demo/grc/grc20 package grc20 import ( "std" "gno.land/p/demo/grc/exts" ) type GRC20 interface { exts.TokenMetadata TotalSupply() uint64 BalanceOf(account std.Address) uint64 Transfer(to std.Address, amount uint64) error Allowance(owner, spender std.Address) uint64 Approve(spender std.Address, amount uint64) error TransferFrom(from, to std.Address, amount uint64) error }
package bar20 import "gno.land/r/demo/grc20reg" var Bank, adm = grc20.NewBank("Bar", "BAR", 4) var UserBanker = grc20.PrevRealmBanker(Bank) func init() { grc20reg.Register(Bank, "") } func Faucet() string { caller := std.GetOrignCaller() if err := adm.Mint(caller, 1_000_000); err != nil { return "error: " + err.Error() } return "OK" }
package grc20reg import ( "std" "gno.land/p/demo/grc/grc20" ) var registry = avl.NewTree() // rlmPath[.slug] -> *grc20.Bank func Register(bank *grc20.Bank, slug string) { rlmPath := std.PrevRealm().PkgPath() key := fqname.Construct(rlmPath, slug) registry.Set(key, bank) std.Emit(registerEvent, "pkgpath", rlmPath, "slug", slug) } func Get(key string) *grc20.Bank { bank, ok := registry.Get(key) if !ok { return nil } return bank.(*grc20.Bank) }
package wugnot import ( "std" "strings" "gno.land/p/demo/grc/grc20" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var Bank, adm = grc20.NewBank("wrapped GNOT", "wugnot", 0) func Deposit() { caller := std.PrevRealm().Addr() sent := std.GetOrigSend() amount := sent.AmountOf("ugnot") require(uint64(amount) >= ugnotMinDeposit, ufmt.Sprintf("Deposit below minimum: %d/%d ugnot.", amount, ugnotMinDeposit)) checkErr(adm.Mint(caller, uint64(amount))) } // Withdraw...
package foo20 import ( "std" "strings" "gno.land/p/demo/grc/grc20" "gno.land/r/demo/grc20reg" ) var instances avl.Tree // symbol -> instance func New(name, symbol string, decimals uint, initialMint, faucet uint64) { admin := std.PrevRealm().Addr() bank := grc20.NewBank(name, symbol, decimals) bank.Mint(admin, initialMint) inst := instance{bank: bank, admin: ownable.NewWithAddress(admin), faucet: faucet} instances.Set(symbol, &inst) grc20reg.Register(bank, symbol) } func Bank(symbol string) *grc20.Bank { return mustGetInstance(symbol).bank }
package dex func (dex *DEX) PlaceOrder(tokenFrom, tokenTo *grc20.Bank, amount uint64, isBuy bool) int { trader, contract := std.PrevRealm().Addr(), std.CurrentRealm().Addr() userBanker := grc20.AccountBanker(tokenFrom, "") allowance := userBanker.Allowance(trader, contract) require(allowance >= amount, "insufficient allowance") err := userBanker.TransferFrom(trader, contract, amount) checkErr(err, "cannot retrieve tokens from allowance") order := &Order{trader: trader, tokenFrom: tokenFrom, tokenTo: tokenTo, amount: amount, isBuy: isBuy} dex.Append(order) std.Emit( "order_placed", "trader", trader.String(), "tokenFrom", tokenFrom.GetName(), "tokenTo", tokenTo.GetName(), "amount", ufmt.Sprintf("%d", amount), ) return dex.matchPairOrders(tokenFrom, tokenTo) }
package dex func (dex *DEX) matchPairOrders(tokenFrom, tokenTo *grc20.Bank) int { matched := 0 orders.Iterate("", "", func(key1 string, value interface{}) bool { orders.Iterate("", "", func(key2 string, value2 interface{}) bool { if order1.isBuy != order2.isBuy && order1.tokenFrom == order2.tokenTo && order1.tokenTo == order2.tokenFrom { amount := min(order1.amount, order2.amount) order1.amount -= amount order2.amount -= amount banker1 := grc20.AccountBanker(order1.tokenFrom, "") banker2 := grc20.AccountBanker(order2.tokenFrom, "") banker1.Transfer(order2.trader, amount) banker2.Transfer(order1.trader, amount) matched++ std.Emit("trade_executed" /*...*/) } }) }) return matched }
package dao // Voter defines the needed methods for a voting system type Voter interface { // IsAccepted indicates if the voting process had been accepted IsAccepted(voters []std.Address) bool // IsFinished indicates if the voting process is finished IsFinished(voters []std.Address) bool // Vote adds a new vote to the voting system Vote(voters []std.Address, caller std.Address, flag string) // Status returns a human friendly string describing how the voting process is going Status(voters []std.Address) string }
package board type Post struct { id int Author std.Address Name string Comments []Post } type Board struct { posts []Post } func (b *Board) Post(name, body string) Post { caller := std.GetOrigCaller() post := Post{len(b.posts), caller, name, body} b.posts = append(b.posts, post) return post }
package board type Post struct{} type Board struct{} func (b *Board) Post(name, body string) Post func (b *Board) Comment(id string, body string) { if id < 0 || id >= len(b.posts) { panic("invalid id") } caller := std.GetOrigCaller() post := &b.posts[id] comment := Post{len(post.Comment), caller, "", body} post.Comment = append(post.Comment, post) }
package blog import "time" type Post struct { id int Date time.Time Name string Comments []Post } var postid int var posts []Post func Post(name, body string) Post { post := Post{postid, time.Now(), name, body} posts = append(posts, post) postid++ return post }
package blog type Post struct{} var ( adminAuthor string = std.GetOrginCaller() // Set deployer as admin postid int posts []Post ) func Post(name, body string) Post { // Assert caller is admin caller := std.GetOrigCaller() if caller != adminAuthor { panic("unauthorized") } // Add post ... }