Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: grc20 dynamic call with registered interface #1275

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions examples/gno.land/r/x/grc20_dynamic_call/bar/bar.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package bar

import (
"std"
"strings"

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

var (
bar *grc20.AdminToken
admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // TODO: helper to change admin
)

func init() {
bar = grc20.NewAdminToken("Bar", "BAR", 4)
bar.Mint(admin, 1000000*10000) // @administrator (1M)
bar.Mint("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", 10000*10000) // @manfred (10k)

bar.Mint("g1tkmrcu9m0xjddxh0c29r3zac8m2yjwaytlvh6u", 1000*10000) // REALM_gno.land/r/x/grc20_dynamic_call/registry
}

// method proxies as public functions.
//

// getters.

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

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

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

// setters.

func Transfer(to users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
err := bar.Transfer(caller, to.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

func Approve(spender users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
err := bar.Approve(caller, spender.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

func TransferFrom(from, to users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
err := bar.TransferFrom(caller, from.Resolve(), to.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

// faucet.

func Faucet() {
// FIXME: add limits?
// FIXME: add payment in gnot?
caller := std.PrevRealm().Addr()
err := bar.Mint(caller, 1000*10000) // 1k
if err != nil {
panic(err.Error())
}
}

// administration.

func Mint(address users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
assertIsAdmin(caller)
err := bar.Mint(address.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

func Burn(address users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
assertIsAdmin(caller)
err := bar.Burn(address.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

// render.
//

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

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

func assertIsAdmin(address std.Address) {
if address != admin {
panic("restricted access")
}
}
7 changes: 7 additions & 0 deletions examples/gno.land/r/x/grc20_dynamic_call/bar/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/r/x/grc20_dynamic_call/bar

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

import (
"std"
"strings"

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

var (
foo *grc20.AdminToken
admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // TODO: helper to change admin
)

func init() {
foo = grc20.NewAdminToken("Foo", "FOO", 4)
foo.Mint(admin, 1000000*10000) // @administrator (1M)
foo.Mint("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", 10000*10000) // @manfred (10k)

foo.Mint("g1tkmrcu9m0xjddxh0c29r3zac8m2yjwaytlvh6u", 1000*10000) // REALM_gno.land/r/x/grc20_dynamic_call/registry
}

// method proxies as public functions.
//

// getters.

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

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

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

// setters.

func Transfer(to users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
err := foo.Transfer(caller, to.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

func Approve(spender users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
err := foo.Approve(caller, spender.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

func TransferFrom(from, to users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
err := foo.TransferFrom(caller, from.Resolve(), to.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

// faucet.

func Faucet() {
// FIXME: add limits?
// FIXME: add payment in gnot?
caller := std.PrevRealm().Addr()
err := foo.Mint(caller, 1000*10000) // 1k
if err != nil {
panic(err.Error())
}
}

// administration.

func Mint(address users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
assertIsAdmin(caller)
err := foo.Mint(address.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

func Burn(address users.AddressOrName, amount uint64) {
caller := std.PrevRealm().Addr()
assertIsAdmin(caller)
err := foo.Burn(address.Resolve(), amount)
if err != nil {
panic(err.Error())
}
}

// render.
//

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

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

func assertIsAdmin(address std.Address) {
if address != admin {
panic("restricted access")
}
}
7 changes: 7 additions & 0 deletions examples/gno.land/r/x/grc20_dynamic_call/foo/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/r/x/grc20_dynamic_call/foo

require (
gno.land/p/demo/grc/grc20 v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/r/demo/users v0.0.0-latest
)
6 changes: 6 additions & 0 deletions examples/gno.land/r/x/grc20_dynamic_call/registry/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module gno.land/r/x/grc20_dynamic_call/registry

require (
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/r/demo/users v0.0.0-latest
)
80 changes: 80 additions & 0 deletions examples/gno.land/r/x/grc20_dynamic_call/registry/registry.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package registry

import (
"std"

"gno.land/p/demo/ufmt"

"gno.land/r/demo/users"
)

const (
APPROVED_UNREGISTER_CALLER = "g1sqt92sa06ugh8nlt98kyghw83qy84paf4csyh6" // like admin
)

type GRC20Interface interface {
Transfer() func(to users.AddressOrName, amount uint64)
TransferFrom() func(from, to users.AddressOrName, amount uint64)
BalanceOf() func(owner users.AddressOrName) uint64
}

var (
registered = make(map[string]GRC20Interface)
)

func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) {
_, found := registered[pkgPath]
if found {
panic(ufmt.Sprintf("GRC20(pkgPath:%s) already registered", pkgPath))
}

registered[pkgPath] = igrc20
}

func UnregisterGRC20Interface(pkgPath string) {
// do not allow realm to unregister
std.AssertOriginCall()

caller := std.GetOrigCaller()
if caller != APPROVED_UNREGISTER_CALLER {
panic("unauthorized")
}

_, found := registered[pkgPath]
if found {
delete(registered, pkgPath)
}
}

func BalanceOfByInterface(pkgPath string, owner std.Address) uint64 {
igrc20, found := registered[pkgPath]
if !found {
return false
}

return igrc20.BalanceOf()(users.AddressOrName(owner))
}

func TransferByInterface(pkgPath string, to std.Address, amount uint64) bool {
igrc20, found := registered[pkgPath]
if !found {
return false
}

// will panic if transfer fails
igrc20.Transfer()(users.AddressOrName(to), amount)

return true
}

func TransferFromByInterface(pkgPath string, from, to std.Address, amount uint64) bool {
igrc20, found := registered[pkgPath]
if !found {
return false
}

// will panic if transferFrom fails
igrc20.TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount)

return true
}
Loading
Loading