Search Apps Documentation Source Content File Folder Download Copy Actions Download

ownable.gno

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