Search Apps Documentation Source Content File Folder Download Copy Actions Download

ownable.gno

3.20 Kb · 126 lines
  1package ownable
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6)
  7
  8const OwnershipTransferEvent = "OwnershipTransfer"
  9
 10// Ownable is meant to be used as a top-level object to make your contract ownable OR
 11// being embedded in a Gno object to manage per-object ownership.
 12// Ownable is safe to export as a top-level object.
 13//
 14// The auth mode (current-realm vs previous-realm) is decided at construction time:
 15//   - New() and NewWithAddress() use current-realm auth.
 16//   - NewWithOrigin() and NewWithAddressByPrevious() use previous-realm auth.
 17//
 18// All ownership operations (TransferOwnership, DropOwnership, Owned, AssertOwned)
 19// automatically use the configured auth mode.
 20type Ownable struct {
 21	owner    address
 22	previous bool // if true, ownership is checked against the previous realm
 23}
 24
 25// New creates an Ownable owned by the current realm, using current-realm auth.
 26func New() *Ownable {
 27	return &Ownable{
 28		owner: runtime.CurrentRealm().Address(),
 29	}
 30}
 31
 32// NewWithOrigin creates an Ownable owned by the origin caller (the user who
 33// initiated the transaction). Must be called from init() where
 34// PreviousRealm() is the origin caller. Uses previous-realm auth.
 35func NewWithOrigin() *Ownable {
 36	origin := runtime.OriginCaller()
 37	previous := runtime.PreviousRealm()
 38	if origin != previous.Address() {
 39		panic("NewWithOrigin() should be called from init() where std.PreviousRealm() is origin")
 40	}
 41	return &Ownable{
 42		owner:    origin,
 43		previous: true,
 44	}
 45}
 46
 47// NewWithAddress creates an Ownable with the given address as owner,
 48// using current-realm auth.
 49func NewWithAddress(addr address) *Ownable {
 50	return &Ownable{
 51		owner: addr,
 52	}
 53}
 54
 55// NewWithAddressByPrevious creates an Ownable with the given address as owner,
 56// using previous-realm auth.
 57func NewWithAddressByPrevious(addr address) *Ownable {
 58	return &Ownable{
 59		owner:    addr,
 60		previous: true,
 61	}
 62}
 63
 64// Owned returns true if the caller is the owner, according to the configured auth mode.
 65func (o *Ownable) Owned() bool {
 66	if o == nil {
 67		return false
 68	}
 69	if o.previous {
 70		return runtime.PreviousRealm().Address() == o.owner
 71	}
 72	return runtime.CurrentRealm().Address() == o.owner
 73}
 74
 75// AssertOwned panics if the caller is not the owner, according to the configured auth mode.
 76func (o *Ownable) AssertOwned() {
 77	if !o.Owned() {
 78		panic(ErrUnauthorized)
 79	}
 80}
 81
 82// TransferOwnership transfers ownership of the Ownable to a new address.
 83// Uses the configured auth mode to verify the caller.
 84func (o *Ownable) TransferOwnership(newOwner address) error {
 85	if !o.Owned() {
 86		return ErrUnauthorized
 87	}
 88
 89	if !newOwner.IsValid() {
 90		return ErrInvalidAddress
 91	}
 92
 93	prevOwner := o.owner
 94	o.owner = newOwner
 95	chain.Emit(
 96		OwnershipTransferEvent,
 97		"from", prevOwner.String(),
 98		"to", newOwner.String(),
 99	)
100
101	return nil
102}
103
104// DropOwnership removes the owner, effectively disabling any owner-related actions.
105// Uses the configured auth mode to verify the caller.
106func (o *Ownable) DropOwnership() error {
107	if !o.Owned() {
108		return ErrUnauthorized
109	}
110	prevOwner := o.owner
111	o.owner = ""
112	chain.Emit(
113		OwnershipTransferEvent,
114		"from", prevOwner.String(),
115		"to", "",
116	)
117	return nil
118}
119
120// Owner returns the owner address from Ownable.
121func (o *Ownable) Owner() address {
122	if o == nil {
123		return address("")
124	}
125	return o.owner
126}