Skip to content

Commit

Permalink
feat: add p/demo/ownable (gnolang#1314)
Browse files Browse the repository at this point in the history
  • Loading branch information
leohhhn authored and gfanton committed Nov 9, 2023
1 parent 0afdba3 commit 8a6c089
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 0 deletions.
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)
}
}

0 comments on commit 8a6c089

Please sign in to comment.