Skip to content

Commit

Permalink
feat: grc20 support
Browse files Browse the repository at this point in the history
Signed-off-by: Norman Meier <[email protected]>
  • Loading branch information
n0izn0iz committed Aug 1, 2023
1 parent 6488680 commit ebea17a
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 4 deletions.
5 changes: 5 additions & 0 deletions examples/gno.land/r/demo/dao_realm/dao_realm.gno
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"gno.land/p/demo/daodao/voting_group"
"gno.land/r/demo/groups"
modboards "gno.land/r/demo/modboards"
tori "gno.land/r/demo/tori"
)

var (
Expand Down Expand Up @@ -49,6 +50,10 @@ func init() {
registry.Register(modboards.NewCreateBoardHandler())
registry.Register(modboards.NewDeletePostHandler())
modboards.CreateBoard(mainBoardName)

// TODO: replace with grc20_admin_registry
registry.Register(tori.NewMintToriHandler())
registry.Register(tori.NewBurnToriHandler())
}

func Render(path string) string {
Expand Down
1 change: 1 addition & 0 deletions examples/gno.land/r/demo/dao_realm/gno.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ require (
"gno.land/p/demo/daodao/voting_group" v0.0.0-latest
"gno.land/r/demo/groups" v0.0.0-latest
"gno.land/r/demo/modboards" v0.0.0-latest
"gno.land/r/demo/tori" v0.0.0-latest
)
6 changes: 6 additions & 0 deletions examples/gno.land/r/demo/grc20_registry/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module gno.land/r/demo/grc20_registry

require (
"gno.land/p/demo/avl" v0.0.0-latest
"gno.land/p/demo/grc/grc20" v0.0.0-latest
)
31 changes: 31 additions & 0 deletions examples/gno.land/r/demo/grc20_registry/grc20_registry.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package grc20_registry

import (
"std"

"gno.land/p/demo/avl"
"gno.land/p/demo/grc/grc20"
)

var (
registry = avl.NewTree() // addr -> IGRC20
lol grc20.IGRC20
)

func Register(token grc20.IGRC20) {
caller := std.PrevRealm().Addr()
// registry.Set(caller.String(), token)
lol = token
}

func Get(addr std.Address) (grc20.IGRC20, bool) {
coinI, ok := registry.Get(addr.String())
if !ok {
return nil, false
}
coin, ok := coinI.(grc20.IGRC20)
if !ok {
panic("should not happen")
}
return coin, true
}
8 changes: 8 additions & 0 deletions examples/gno.land/r/demo/tori/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module gno.land/r/demo/tori

require (
"gno.land/p/demo/ufmt" v0.0.0-latest
"gno.land/p/demo/grc/grc20" v0.0.0-latest
"gno.land/r/demo/users" v0.0.0-latest
"gno.land/r/demo/grc20_registry" v0.0.0-latest
)
136 changes: 136 additions & 0 deletions examples/gno.land/r/demo/tori/messages.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package tori

import (
"encoding/binary"
"std"
"strconv"
"strings"

"gno.land/p/demo/binutils"
"gno.land/p/demo/daodao/interfaces"
"gno.land/r/demo/users"
)

type ExecutableMessageMintTori struct {
dao_interfaces.ExecutableMessage

Address users.AddressOrName
Amount uint64
}

func (msg *ExecutableMessageMintTori) Type() string {
return "MintTori"
}

func (msg *ExecutableMessageMintTori) String() string {
var ss []string
ss = append(ss, msg.Type())
s := "Address: " + string(msg.Address) + "\n"
s += "Amount: " + strconv.FormatUint(msg.Amount, 10)
ss = append(ss, s)
return strings.Join(ss, "\n---\n")
}

func (msg *ExecutableMessageMintTori) Binary() []byte {
b := []byte{}
b = append(b, binutils.EncodeLengthPrefixedStringUint16BE(msg.Type())...)
b = append(b, binutils.EncodeLengthPrefixedStringUint16BE(string(msg.Address))...)
b = binary.BigEndian.AppendUint64(b, msg.Amount)
return b
}

func ExecutableMessageMintToriFromBinary(b []byte) *ExecutableMessageMintTori {
msg := &ExecutableMessageMintTori{}
t, b := binutils.MustDecodeLengthPrefixedStringUint16BE(b)
if t != msg.Type() {
panic("invalid type")
}
var addr string
addr, b = binutils.MustDecodeLengthPrefixedStringUint16BE(b)
msg.Address = users.AddressOrName(addr)
msg.Amount, b = binary.BigEndian.Uint64(b), b[8:]
return msg
}

type MintToriHandler struct {
dao_interfaces.MessageHandler
}

func NewMintToriHandler() *MintToriHandler {
return &MintToriHandler{}
}

func (h *MintToriHandler) Execute(imsg dao_interfaces.ExecutableMessage) {
msg := imsg.(*ExecutableMessageMintTori)
Mint(msg.Address, msg.Amount)
}

func (h *MintToriHandler) Type() string {
return ExecutableMessageMintTori{}.Type()
}

func (h *MintToriHandler) FromBinary(b []byte) dao_interfaces.ExecutableMessage {
return ExecutableMessageMintToriFromBinary(b)
}

type ExecutableMessageBurnTori struct {
dao_interfaces.ExecutableMessage

Address users.AddressOrName
Amount uint64
}

func (msg *ExecutableMessageBurnTori) Type() string {
return "BurnTori"
}

func (msg *ExecutableMessageBurnTori) String() string {
var ss []string
ss = append(ss, msg.Type())
s := "Address: " + string(msg.Address) + "\n"
s += "Amount: " + strconv.FormatUint(msg.Amount, 10)
ss = append(ss, s)
return strings.Join(ss, "\n---\n")
}

func (msg *ExecutableMessageBurnTori) Binary() []byte {
b := []byte{}
b = append(b, binutils.EncodeLengthPrefixedStringUint16BE(msg.Type())...)
b = append(b, binutils.EncodeLengthPrefixedStringUint16BE(string(msg.Address))...)
b = binary.BigEndian.AppendUint64(b, msg.Amount)
return b
}

func ExecutableMessageBurnToriFromBinary(b []byte) *ExecutableMessageBurnTori {
msg := &ExecutableMessageBurnTori{}
t, b := binutils.MustDecodeLengthPrefixedStringUint16BE(b)
if t != msg.Type() {
panic("invalid type")
}
var addr string
addr, b = binutils.MustDecodeLengthPrefixedStringUint16BE(b)
msg.Address = users.AddressOrName(addr)
msg.Amount, b = binary.BigEndian.Uint64(b), b[8:]
return msg
}

type BurnToriHandler struct {
dao_interfaces.MessageHandler
}

func NewBurnToriHandler() *BurnToriHandler {
return &BurnToriHandler{}
}

func (h *BurnToriHandler) Execute(imsg dao_interfaces.ExecutableMessage) {
msg := imsg.(*ExecutableMessageBurnTori)
Burn(msg.Address, msg.Amount)
}

func (h *BurnToriHandler) Type() string {
return ExecutableMessageBurnTori{}.Type()
}

func (h *BurnToriHandler) FromBinary(b []byte) dao_interfaces.ExecutableMessage {
return ExecutableMessageBurnToriFromBinary(b)
}
113 changes: 113 additions & 0 deletions examples/gno.land/r/demo/tori/tori.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package tori

import (
"std"
"strings"

"gno.land/p/demo/grc/grc20"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/grc20_registry"
"gno.land/r/demo/users"
)

var (
tori *grc20.AdminToken
userTori grc20.IGRC20
admin std.Address = std.DerivePkgAddr("gno.land/r/demo/dao_realm") // TODO: helper to change admin
)

func init() {
tori = grc20.NewAdminToken("Tori", "TORI", 6)
userTori = tori.GRC20()
grc20_registry.Register(userTori) // found another bug, calling this sets some grc20 internal object's pkgid to the registry realm id
}

// method proxies as public functions.
//

// getters.

func TotalSupply() uint64 {
return tori.TotalSupply()
}

func BalanceOf(owner users.AddressOrName) uint64 {
balance, err := tori.BalanceOf(owner.Resolve())
if err != nil {
panic(err)
}
return balance
}

func Allowance(owner, spender users.AddressOrName) uint64 {
allowance, err := tori.Allowance(owner.Resolve(), spender.Resolve())
if err != nil {
panic(err)
}
return allowance
}

// setters.

func Transfer(to users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
tori.Transfer(caller, to.Resolve(), amount)
}

func Approve(spender users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
tori.Approve(caller, spender.Resolve(), amount)
}

func TransferFrom(from, to users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
tori.TransferFrom(caller, from.Resolve(), to.Resolve(), amount)
}

// faucet.

func Faucet() {
// FIXME: add limits?
// FIXME: add payment in gnot?
caller := std.PrevRealm().Addr()
tori.Mint(caller, 1000*10000) // 1k
}

// administration.

func Mint(address users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
assertIsAdmin(caller)
tori.Mint(address.Resolve(), amount)
}

func Burn(address users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
assertIsAdmin(caller)
tori.Burn(address.Resolve(), amount)
}

// render.
//

func Render(path string) string {
parts := strings.Split(path, "/")
c := len(parts)

switch {
case path == "":
return tori.RenderHome()
case c == 2 && parts[0] == "balance":
owner := users.AddressOrName(parts[1])
balance, _ := tori.BalanceOf(owner.Resolve())
return ufmt.Sprintf("%d\n", balance)
default:
return "404\n"
}
}

func assertIsAdmin(address std.Address) {
if address != admin {
panic("restricted access")
}
}
20 changes: 16 additions & 4 deletions gnovm/pkg/gnolang/realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,16 @@ func (pid PkgID) Bytes() []byte {
return pid.Hashlet[:]
}

var pathsFromIds = make(map[string]string)

func PkgIDFromPkgPath(path string) PkgID {
return PkgID{HashBytes([]byte(path))}
id := PkgID{HashBytes([]byte(path))}
pathsFromIds[id.String()] = path
return id
}

func PkgPathFromPkgID(id PkgID) string {
return pathsFromIds[id.String()]
}

func ObjectIDFromPkgPath(path string) ObjectID {
Expand Down Expand Up @@ -157,7 +165,12 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) {
return // do nothing.
}
if po.GetObjectID().PkgID != rlm.ID {
panic("cannot modify external-realm or non-realm object")
opid := po.GetObjectID().PkgID
prettyName := PkgPathFromPkgID(opid)
if prettyName == "" {
prettyName = opid.String()
}
panic(fmt.Sprintf("cannot modify external-realm or non-realm object: object pkg path: %s, realm: %s", prettyName, rlm.Path))
}
// From here on, po is real (not new-real).
// Updates to .newCreated/.newEscaped /.newDeleted made here. (first gen)
Expand All @@ -179,8 +192,7 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) {
fmt.Println("DidUpdate - mark escaped")
rlm.MarkNewEscaped(co)
}
}
if co.GetIsReal() {
} else if co.GetIsReal() {
if shouldDebug {
fmt.Println("MarkDirty co", co)
}
Expand Down

0 comments on commit ebea17a

Please sign in to comment.