diff --git a/examples/gno.land/r/x/grc20_dynamic_call/bar/bar.gno b/examples/gno.land/r/x/grc20_dynamic_call/bar/bar.gno new file mode 100644 index 00000000000..1638971f63d --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/bar/bar.gno @@ -0,0 +1,130 @@ +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) +} + +// 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) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := bar.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + 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) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + // caller := std.PrevRealm().Addr() + caller := std.GetOrigCaller() + err := bar.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err) + } +} + +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) + } +} + +// 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) + } +} + +// 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) + } +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := bar.Burn(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +// 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") + } +} diff --git a/examples/gno.land/r/x/grc20_dynamic_call/bar/gno.mod b/examples/gno.land/r/x/grc20_dynamic_call/bar/gno.mod new file mode 100644 index 00000000000..97dc76d73b3 --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/bar/gno.mod @@ -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 +) diff --git a/examples/gno.land/r/x/grc20_dynamic_call/baz/baz.gno b/examples/gno.land/r/x/grc20_dynamic_call/baz/baz.gno new file mode 100644 index 00000000000..f0393edac52 --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/baz/baz.gno @@ -0,0 +1,130 @@ +package baz + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "gno.land/r/demo/users" +) + +var ( + baz *grc20.AdminToken + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // TODO: helper to change admin +) + +func init() { + baz = grc20.NewAdminToken("Baz", "BAZ", 4) + baz.Mint(admin, 1000000*10000) // @administrator (1M) + baz.Mint("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", 10000*10000) // @manfred (10k) +} + +// method proxies as public functions. +// + +// getters. + +func TotalSupply() uint64 { + return baz.TotalSupply() +} + +func BalanceOf(owner users.AddressOrName) uint64 { + balance, err := baz.BalanceOf(owner.Resolve()) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := baz.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + return allowance +} + +// setters. + +func Transfer(to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := baz.Transfer(caller, to.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + // caller := std.PrevRealm().Addr() + caller := std.GetOrigCaller() + err := baz.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := baz.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) + if err != nil { + panic(err) + } +} + +// faucet. + +func Faucet() { + // FIXME: add limits? + // FIXME: add payment in gnot? + caller := std.PrevRealm().Addr() + err := baz.Mint(caller, 1000*10000) // 1k + if err != nil { + panic(err) + } +} + +// administration. + +func Mint(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := baz.Mint(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := baz.Burn(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +// render. +// + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return baz.RenderHome() + case c == 2 && parts[0] == "balance": + owner := users.AddressOrName(parts[1]) + balance, _ := baz.BalanceOf(owner.Resolve()) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} diff --git a/examples/gno.land/r/x/grc20_dynamic_call/baz/gno.mod b/examples/gno.land/r/x/grc20_dynamic_call/baz/gno.mod new file mode 100644 index 00000000000..3b0dc9b57bc --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/baz/gno.mod @@ -0,0 +1,7 @@ +module gno.land/r/x/grc20_dynamic_call/baz + +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 +) diff --git a/examples/gno.land/r/x/grc20_dynamic_call/foo/foo.gno b/examples/gno.land/r/x/grc20_dynamic_call/foo/foo.gno new file mode 100644 index 00000000000..7377bdc744c --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/foo/foo.gno @@ -0,0 +1,130 @@ +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) +} + +// 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) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := foo.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + 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) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + // caller := std.PrevRealm().Addr() + caller := std.GetOrigCaller() + err := foo.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err) + } +} + +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) + } +} + +// 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) + } +} + +// 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) + } +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := foo.Burn(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +// 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") + } +} diff --git a/examples/gno.land/r/x/grc20_dynamic_call/foo/gno.mod b/examples/gno.land/r/x/grc20_dynamic_call/foo/gno.mod new file mode 100644 index 00000000000..171f60edf53 --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/foo/gno.mod @@ -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 +) diff --git a/examples/gno.land/r/x/grc20_dynamic_call/registry/gno.mod b/examples/gno.land/r/x/grc20_dynamic_call/registry/gno.mod new file mode 100644 index 00000000000..394b4395063 --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/registry/gno.mod @@ -0,0 +1,9 @@ +module gno.land/r/x/grc20_dynamic_call/registry + +require ( + gno.land/r/demo/users v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/bar v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/baz v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/foo v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/wrapper v0.0.0-latest +) diff --git a/examples/gno.land/r/x/grc20_dynamic_call/registry/registry.gno b/examples/gno.land/r/x/grc20_dynamic_call/registry/registry.gno new file mode 100644 index 00000000000..21be84253df --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/registry/registry.gno @@ -0,0 +1,101 @@ +package registry + +import ( + "std" + + "gno.land/r/demo/users" +) + +const APPROVED_UNREGISTER_CALLER = "g1sqt92sa06ugh8nlt98kyghw83qy84paf4csyh6" + +var registered = []GRC20Pair{} + +type GRC20Interface interface { + Transfer() func(to users.AddressOrName, amount uint64) + TransferFrom() func(from, to users.AddressOrName, amount uint64) + BalanceOf() func(owner users.AddressOrName) uint64 +} + +type GRC20Pair struct { + pkgPath string + igrc20 GRC20Interface +} + +func findGRC20(pkgPath string) (int, bool) { + for i, pair := range registered { + if pair.pkgPath == pkgPath { + return i, true + } + } + + return -1, false +} + +func appendGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + registered = append(registered, GRC20Pair{pkgPath: pkgPath, igrc20: igrc20}) +} + +func removeGRC20Interface(pkgPath string) { + i, found := findGRC20(pkgPath) + if !found { + return + } + + registered = append(registered[:i], registered[i+1:]...) +} + +func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + _, found := findGRC20(pkgPath) + if found { + panic("GRC20 already registered") + } + + appendGRC20Interface(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 := findGRC20(pkgPath) + if found { + removeGRC20Interface(pkgPath) + } +} + +func TransferByInterfaceName(pkgPath string, to std.Address, amount uint64) bool { + i, found := findGRC20(pkgPath) + if !found { + return false + } + + registered[i].igrc20.Transfer()(users.AddressOrName(to), amount) + + return true +} + +func TransferFromByInterfaceName(pkgPath string, from, to std.Address, amount uint64) bool { + i, found := findGRC20(pkgPath) + if !found { + return false + } + + registered[i].igrc20.TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + + return true +} + +func BalanceOfByInterfaceName(pkgPath string, owner std.Address) uint64 { + i, found := findGRC20(pkgPath) + if !found { + return 0 + } + + balance := registered[i].igrc20.BalanceOf()(users.AddressOrName(owner)) + return balance +} diff --git a/examples/gno.land/r/x/grc20_dynamic_call/registry/registry_test.gno b/examples/gno.land/r/x/grc20_dynamic_call/registry/registry_test.gno new file mode 100644 index 00000000000..d835723095d --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/registry/registry_test.gno @@ -0,0 +1,170 @@ +package registry + +import ( + "std" + "testing" + + "gno.land/r/x/grc20_dynamic_call/bar" + "gno.land/r/x/grc20_dynamic_call/baz" + "gno.land/r/x/grc20_dynamic_call/foo" + + _ "gno.land/r/x/grc20_dynamic_call/wrapper" + + "gno.land/p/demo/testutils" + + "gno.land/r/demo/users" +) + +var ( + cRealmAddr std.Address + + transferTo std.Address + + transferFromFrom std.Address + transferFromTo std.Address +) + +func init() { + cRealmAddr = std.CurrentRealm().Addr() + + transferTo = testutils.TestAddress("transferTo") + + transferFromFrom = transferTo // use transferTo as transferFromFrom + transferFromTo = testutils.TestAddress("transferFromTo") + + std.TestSetOrigCaller(cRealmAddr) + foo.Faucet() + bar.Faucet() + baz.Faucet() +} + +func TestTransferByNameFOO(t *testing.T) { + fooBalance := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferTo) + if fooBalance != 0 { + t.Fatal("transferTo should have 0 FOO but", fooBalance) + } + + TransferByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferTo, 12345) + + fooBalance = BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferTo) + if fooBalance != 12345 { + t.Fatal("transferTo should have 12345 FOO, but", fooBalance) + } +} + +func TestTransferByNameBAR(t *testing.T) { + barBalance := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferTo) + if barBalance != 0 { + t.Fatal("transferTo should have 0 BAR but", barBalance) + } + + TransferByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferTo, 12345) + + barBalance = BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferTo) + if barBalance != 12345 { + t.Fatal("transferTo should have 12345 BAR, but", barBalance) + } +} + +func TestTransferByNameBAZ(t *testing.T) { + bazBalance := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferTo) + if bazBalance != 0 { + t.Fatal("transferTo should have 0 BAZ but", bazBalance) + } + + TransferByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferTo, 12345) + + bazBalance = BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferTo) + if bazBalance != 12345 { + t.Fatal("transferTo should have 12345 BAZ, but", bazBalance) + } +} + +func TestTransferFromByNameFOO(t *testing.T) { + fooBalanceTo := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferFromTo) + if fooBalanceTo != 0 { + t.Fatal("transferFromTo should have 0 FOO, but", fooBalanceTo) + } + + std.TestSetOrigCaller(transferFromFrom) + foo.Approve(users.AddressOrName(cRealmAddr), 5) + TransferFromByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferFromFrom, transferFromTo, 3) + + fooBalanceTo = BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferFromTo) + if fooBalanceTo != 3 { + t.Fatal("transferFromTo should have 3 FOO, but", fooBalanceTo) + } + + fooBalanceFrom := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/foo", transferFromFrom) + if fooBalanceFrom != 12342 { + t.Fatal("transferFromFrom should have 12342 FOO, but", fooBalanceFrom) + } +} + +func TestTransferFromByNameBAR(t *testing.T) { + barBalanceTo := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferFromTo) + if barBalanceTo != 0 { + t.Fatal("transferFromTo should have 0 BAR, but", barBalanceTo) + } + + std.TestSetOrigCaller(transferFromFrom) + bar.Approve(users.AddressOrName(cRealmAddr), 5) + TransferFromByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferFromFrom, transferFromTo, 3) + + barBalanceTo = BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferFromTo) + if barBalanceTo != 3 { + t.Fatal("transferFromTo should have 3 BAR, but", barBalanceTo) + } + + barBalanceFrom := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/bar", transferFromFrom) + if barBalanceFrom != 12342 { + t.Fatal("transferFromFrom should have 12342 BAR, but", barBalanceFrom) + } +} + +func TestTransferFromByNameBAZ(t *testing.T) { + bazBalanceTo := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferFromTo) + if bazBalanceTo != 0 { + t.Fatal("transferFromTo should have 0 BAZ, but", bazBalanceTo) + } + + std.TestSetOrigCaller(transferFromFrom) + baz.Approve(users.AddressOrName(cRealmAddr), 5) + TransferFromByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferFromFrom, transferFromTo, 3) + + bazBalanceTo = BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferFromTo) + if bazBalanceTo != 3 { + t.Fatal("transferFromTo should have 3 BAZ, but", bazBalanceTo) + } + + bazBalanceFrom := BalanceOfByInterfaceName("gno.land/r/x/grc20_dynamic_call/baz", transferFromFrom) + if bazBalanceFrom != 12342 { + t.Fatal("transferFromFrom should have 12342 BAZ, but", bazBalanceFrom) + } +} + +func TestUnregisterUnauthorized(t *testing.T) { + shouldPanic(t, func() { UnregisterGRC20Interface("gno.land/r/x/grc20_dynamic_call/foo") }) +} + +func TestUnregisterAuthorized(t *testing.T) { + if len(registered) != 3 { + t.Fatal("should have 3 registered interfaces but", len(registered)) + } + + std.TestSetOrigCaller("g1sqt92sa06ugh8nlt98kyghw83qy84paf4csyh6") + UnregisterGRC20Interface("gno.land/r/x/grc20_dynamic_call/foo") + + if len(registered) != 2 { + t.Fatal("should have 2 registered interfaces") + } +} + +func shouldPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("expected panic") + } + }() + f() +} diff --git a/examples/gno.land/r/x/grc20_dynamic_call/wrapper/gno.mod b/examples/gno.land/r/x/grc20_dynamic_call/wrapper/gno.mod new file mode 100644 index 00000000000..3b0b1b01e37 --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/wrapper/gno.mod @@ -0,0 +1,9 @@ +module gno.land/r/x/grc20_dynamic_call/wrapper + +require ( + gno.land/r/demo/users v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/bar v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/baz v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/foo v0.0.0-latest + gno.land/r/x/grc20_dynamic_call/registry v0.0.0-latest +) diff --git a/examples/gno.land/r/x/grc20_dynamic_call/wrapper/wrapper.gno b/examples/gno.land/r/x/grc20_dynamic_call/wrapper/wrapper.gno new file mode 100644 index 00000000000..6c81c9a6d2e --- /dev/null +++ b/examples/gno.land/r/x/grc20_dynamic_call/wrapper/wrapper.gno @@ -0,0 +1,65 @@ +package wrapper + +import ( + "gno.land/r/demo/users" + + "gno.land/r/x/grc20_dynamic_call/bar" + "gno.land/r/x/grc20_dynamic_call/baz" + "gno.land/r/x/grc20_dynamic_call/foo" + + "gno.land/r/x/grc20_dynamic_call/registry" +) + +type FooTokenInterface struct{} + +func (FooTokenInterface) Transfer() func(to users.AddressOrName, amount uint64) { + return foo.Transfer +} + +func (FooTokenInterface) TransferFrom() func(from, to users.AddressOrName, amount uint64) { + return foo.TransferFrom +} + +func (FooTokenInterface) BalanceOf() func(owner users.AddressOrName) uint64 { + return foo.BalanceOf +} + +var _ registry.GRC20Interface = FooTokenInterface{} + +type BarTokenInterface struct{} + +func (BarTokenInterface) Transfer() func(to users.AddressOrName, amount uint64) { + return bar.Transfer +} + +func (BarTokenInterface) TransferFrom() func(from, to users.AddressOrName, amount uint64) { + return bar.TransferFrom +} + +func (BarTokenInterface) BalanceOf() func(owner users.AddressOrName) uint64 { + return bar.BalanceOf +} + +var _ registry.GRC20Interface = BarTokenInterface{} + +type BazTokenInterface struct{} + +func (BazTokenInterface) Transfer() func(to users.AddressOrName, amount uint64) { + return baz.Transfer +} + +func (BazTokenInterface) TransferFrom() func(from, to users.AddressOrName, amount uint64) { + return baz.TransferFrom +} + +func (BazTokenInterface) BalanceOf() func(owner users.AddressOrName) uint64 { + return baz.BalanceOf +} + +var _ registry.GRC20Interface = BazTokenInterface{} + +func init() { + registry.RegisterGRC20Interface("gno.land/r/x/grc20_dynamic_call/foo", FooTokenInterface{}) + registry.RegisterGRC20Interface("gno.land/r/x/grc20_dynamic_call/bar", BarTokenInterface{}) + registry.RegisterGRC20Interface("gno.land/r/x/grc20_dynamic_call/baz", BazTokenInterface{}) +} diff --git a/gno.land/cmd/gnoland/testdata/grc20_dynamic.txtar b/gno.land/cmd/gnoland/testdata/grc20_dynamic.txtar new file mode 100644 index 00000000000..6756ae5f0ca --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/grc20_dynamic.txtar @@ -0,0 +1,21 @@ +# test for calling grc20 token's function with registering interface + +## start gnoland node +gnoland start + +## faucet foo token by test1 +gnokey maketx call -pkgpath gno.land/r/x/grc20_dynamic_call/foo -func Faucet -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## check test1 foo balance +gnokey maketx call -pkgpath gno.land/r/x/grc20_dynamic_call/registry -func BalanceOfByInterfaceName -args 'gno.land/r/x/grc20_dynamic_call/foo' -args 'g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5' -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout '(10000000 uint64)' + +## approve +gnokey maketx call -pkgpath gno.land/r/x/grc20_dynamic_call/foo -func Approve -args 'g1tkmrcu9m0xjddxh0c29r3zac8m2yjwaytlvh6u' -args '12345' -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## transferFrom foo token using registered interface +gnokey maketx call -pkgpath gno.land/r/x/grc20_dynamic_call/registry -func TransferFromByInterfaceName -args 'gno.land/r/x/grc20_dynamic_call/foo' -args 'g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5' -args 'g12345' -args '12345' -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## check balance after TransferByInterfaceName +gnokey maketx call -pkgpath gno.land/r/x/grc20_dynamic_call/registry -func BalanceOfByInterfaceName -args 'gno.land/r/x/grc20_dynamic_call/foo' -args 'g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5' -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout '(9987655 uint64)' \ No newline at end of file