Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

ownable.gno

3.30 Kb · 127 lines
  1package rbac
  2
  3import (
  4	"chain"
  5	"errors"
  6)
  7
  8const (
  9	OwnershipTransferEvent        = "OwnershipTransfer"
 10	OwnershipTransferStartedEvent = "OwnershipTransferStarted"
 11)
 12
 13// Ownable2Step implements a two-step ownership transfer mechanism.
 14// It requires the new owner to explicitly accept ownership before the transfer is completed,
 15// preventing accidental transfers to incorrect addresses.
 16//
 17// Note: This package does not verify callers. Consuming realms must extract the actual
 18// caller from the live realm context and pass it to these methods.
 19type Ownable2Step struct {
 20	owner        address
 21	pendingOwner address
 22}
 23
 24// newOwnable2StepWithAddress creates a new Ownable2Step instance with addr as owner.
 25func newOwnable2StepWithAddress(addr address) *Ownable2Step {
 26	return &Ownable2Step{
 27		owner:        addr,
 28		pendingOwner: "",
 29	}
 30}
 31
 32// TransferOwnershipBy initiates ownership transfer by setting newOwner as pending owner.
 33// The newOwner must call AcceptOwnershipBy to complete the transfer.
 34//
 35// Errors:
 36//   - ErrUnauthorized: caller is not the current owner
 37//   - ErrInvalidAddress: newOwner is empty or has an invalid format
 38func (o *Ownable2Step) TransferOwnershipBy(newOwner, caller address) error {
 39	if !o.IsOwner(caller) {
 40		return errors.New(ErrUnauthorized)
 41	}
 42
 43	if newOwner == zeroAddress || !newOwner.IsValid() {
 44		return errors.New(ErrInvalidAddress)
 45	}
 46
 47	o.pendingOwner = newOwner
 48
 49	chain.Emit(
 50		OwnershipTransferStartedEvent,
 51		"from", o.owner.String(),
 52		"to", newOwner.String(),
 53	)
 54
 55	return nil
 56}
 57
 58// AcceptOwnershipBy completes the ownership transfer.
 59// Must be called by the pending owner.
 60//
 61// Errors:
 62//   - ErrNoPendingOwner: no ownership transfer is pending
 63//   - ErrPendingUnauthorized: caller is not the pending owner
 64func (o *Ownable2Step) AcceptOwnershipBy(caller address) error {
 65	if o.pendingOwner == zeroAddress {
 66		return errors.New(ErrNoPendingOwner)
 67	}
 68
 69	if !o.IsPendingOwner(caller) {
 70		return errors.New(ErrPendingUnauthorized)
 71	}
 72
 73	prevOwner := o.owner
 74	o.owner = o.pendingOwner
 75	o.pendingOwner = ""
 76
 77	chain.Emit(
 78		OwnershipTransferEvent,
 79		"from", prevOwner.String(),
 80		"to", o.owner.String(),
 81	)
 82
 83	return nil
 84}
 85
 86// DropOwnershipBy removes the owner, disabling all owner-only actions.
 87// This is irreversible - when ownership is dropped, no future owner-only operations can be performed.
 88//
 89// Errors:
 90//   - ErrUnauthorized: caller is not the current owner
 91func (o *Ownable2Step) DropOwnershipBy(caller address) error {
 92	if !o.IsOwner(caller) {
 93		return errors.New(ErrUnauthorized)
 94	}
 95
 96	prevOwner := o.owner
 97	o.owner = ""
 98	o.pendingOwner = ""
 99
100	chain.Emit(
101		OwnershipTransferEvent,
102		"from", prevOwner.String(),
103		"to", "",
104	)
105
106	return nil
107}
108
109// Owner returns the current owner address. Returns empty address if ownership has been dropped.
110func (o *Ownable2Step) Owner() address {
111	return o.owner
112}
113
114// PendingOwner returns the pending owner address during ownership transfer. Returns empty address if no transfer is pending.
115func (o *Ownable2Step) PendingOwner() address {
116	return o.pendingOwner
117}
118
119// IsOwner returns true if the origin caller is the current owner.
120func (o *Ownable2Step) IsOwner(caller address) bool {
121	return o.owner == caller
122}
123
124// IsPendingOwner returns true if the origin caller is the pending owner.
125func (o *Ownable2Step) IsPendingOwner(caller address) bool {
126	return o.pendingOwner == caller
127}