forked from gnolang/gno
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add p/demo/ownable (gnolang#1314)
- Loading branch information
Showing
4 changed files
with
179 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/p/demo/ownable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |