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: add p/demo/ownable #1314

Merged
merged 20 commits into from
Nov 2, 2023
8 changes: 8 additions & 0 deletions examples/gno.land/p/demo/ownable/errors.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ownable

import "errors"

var (
ErrUnauthorized = errors.New("unauthorized; caller is not owner")
ErrInvalidAddress = errors.New("new owner address is invalid")
)
1 change: 1 addition & 0 deletions examples/gno.land/p/demo/ownable/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/demo/ownable
57 changes: 57 additions & 0 deletions examples/gno.land/p/demo/ownable/ownable.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ownable

import (
"std"
)

// Ownable is meant to be used as a top-level object to make your contract ownable OR
// being embedded in a Gno object to manage per-object ownership.
type Ownable struct {
owner std.Address
}

func New() *Ownable {
return &Ownable{
owner: std.GetOrigCaller(),
}
}

// TransferOwnership transfers ownership of the Ownable struct to a new address
func (o *Ownable) TransferOwnership(newOwner std.Address) error {
err := o.CallerIsOwner()
if err != nil {
return err
}

if !newOwner.IsValid() {
return ErrInvalidAddress
}

o.owner = newOwner
return nil
}

// DropOwnership removes the owner, effectively disabling any owner-related actions
// Top-level usage: disables all only-owner actions/functions,
// Embedded usage: behaves like a burn functionality, removing the owner from the struct
func (o *Ownable) DropOwnership() error {
err := o.CallerIsOwner()
if err != nil {
return err
}

o.owner = ""
return nil
}

// CallerIsOwner checks if the caller of the function is the Realm's owner
func (o *Ownable) CallerIsOwner() error {
if std.GetOrigCaller() == o.owner {
return nil
}
return ErrUnauthorized
}

func (o *Ownable) Owner() std.Address {
return o.owner
}
113 changes: 113 additions & 0 deletions examples/gno.land/p/demo/ownable/ownable_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package ownable

import (
"std"
"testing"
)

var (
firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de")
secondCaller = std.Address("g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa")
)

func TestNew(t *testing.T) {
std.TestSetOrigCaller(firstCaller)

result := New()
if firstCaller != result.owner {
t.Fatalf("Expected %s, got: %s\n", firstCaller, result.owner)
}
}

func TestOwner(t *testing.T) {
std.TestSetOrigCaller(firstCaller)

result := New()
resultOwner := result.Owner()

expected := firstCaller
if resultOwner != expected {
t.Fatalf("Expected %s, got: %s\n", expected, result)
}
}

func TestTransferOwnership(t *testing.T) {
std.TestSetOrigCaller(firstCaller)
o := New()

err := o.TransferOwnership(secondCaller)
if err != nil {
t.Fatalf("TransferOwnership failed, %v", err)
}

result := o.Owner()
if secondCaller != result {
t.Fatalf("Expected: %s, got: %s\n", secondCaller, result)
}
}

func TestCallerIsOwner(t *testing.T) {
std.TestSetOrigCaller(firstCaller)

o := New()
unauthorizedCaller := secondCaller

std.TestSetOrigCaller(unauthorizedCaller)

err := o.CallerIsOwner()
if err == nil {
t.Fatalf("Expected %s to not be owner\n", unauthorizedCaller)
}
}

func TestDropOwnership(t *testing.T) {
std.TestSetOrigCaller(firstCaller)

o := New()

err := o.DropOwnership()
if err != nil {
t.Fatalf("DropOwnership failed, %v", err)
}

owner := o.Owner()
if owner != "" {
t.Fatalf("Expected owner to be empty, not %s\n", owner)
}
}

// Errors

func TestErrUnauthorized(t *testing.T) {
std.TestSetOrigCaller(firstCaller)

o := New()

std.TestSetOrigCaller(secondCaller)

err := o.TransferOwnership(firstCaller)
if err != ErrUnauthorized {
t.Fatalf("Should've been ErrUnauthorized, was %v", err)
}

err = o.DropOwnership()
if err != ErrUnauthorized {
t.Fatalf("Should've been ErrUnauthorized, was %v", err)
}
}

func TestErrInvalidAddress(t *testing.T) {
std.TestSetOrigCaller(firstCaller)

o := New()

err := o.TransferOwnership("")
if err != ErrInvalidAddress {
t.Fatalf("Should've been ErrInvalidAddress, was %v", err)
}

err = o.TransferOwnership("10000000001000000000100000000010000000001000000000")
if err != ErrInvalidAddress {
t.Fatalf("Should've been ErrInvalidAddress, was %v", err)
}
}
Loading