package rbac import ( "chain" "errors" ) const ( OwnershipTransferEvent = "OwnershipTransfer" OwnershipTransferStartedEvent = "OwnershipTransferStarted" ) // Ownable2Step implements a two-step ownership transfer mechanism. // It requires the new owner to explicitly accept ownership before the transfer is completed, // preventing accidental transfers to incorrect addresses. // // Note: This package does not verify callers. Consuming realms must extract the actual // caller from the live realm context and pass it to these methods. type Ownable2Step struct { owner address pendingOwner address } // newOwnable2StepWithAddress creates a new Ownable2Step instance with addr as owner. func newOwnable2StepWithAddress(addr address) *Ownable2Step { return &Ownable2Step{ owner: addr, pendingOwner: "", } } // TransferOwnershipBy initiates ownership transfer by setting newOwner as pending owner. // The newOwner must call AcceptOwnershipBy to complete the transfer. // // Parameters: // - newOwner: Non-zero, syntactically valid address to record as the pending owner. // - caller: Address authorized to initiate the transfer; it must equal the current owner. // // Errors: // - ErrUnauthorized: caller is not the current owner // - ErrInvalidAddress: newOwner is empty or has an invalid format // // Returns: // - error: nil when the pending owner is set; otherwise ErrUnauthorized or ErrInvalidAddress. func (o *Ownable2Step) TransferOwnershipBy(newOwner, caller address) error { if !o.IsOwner(caller) { return errors.New(ErrUnauthorized) } if newOwner == zeroAddress || !newOwner.IsValid() { return errors.New(ErrInvalidAddress) } o.pendingOwner = newOwner chain.Emit( OwnershipTransferStartedEvent, "from", o.owner.String(), "to", newOwner.String(), ) return nil } // AcceptOwnershipBy completes the ownership transfer. // Must be called by the pending owner. // // Parameters: // - caller: Address attempting to accept ownership; it must equal the recorded pending owner. // // Errors: // - ErrNoPendingOwner: no ownership transfer is pending // - ErrPendingUnauthorized: caller is not the pending owner // // Returns: // - error: nil when ownership is transferred to caller; otherwise ErrNoPendingOwner or ErrPendingUnauthorized. func (o *Ownable2Step) AcceptOwnershipBy(caller address) error { if o.pendingOwner == zeroAddress { return errors.New(ErrNoPendingOwner) } if !o.IsPendingOwner(caller) { return errors.New(ErrPendingUnauthorized) } prevOwner := o.owner o.owner = o.pendingOwner o.pendingOwner = "" chain.Emit( OwnershipTransferEvent, "from", prevOwner.String(), "to", o.owner.String(), ) return nil } // DropOwnershipBy removes the owner, disabling all owner-only actions. // This is irreversible - when ownership is dropped, no future owner-only operations can be performed. // // Parameters: // - caller: Address requesting the drop; it must equal the current owner. // // Errors: // - ErrUnauthorized: caller is not the current owner // // Returns: // - error: nil when owner and pending owner are cleared; otherwise ErrUnauthorized. func (o *Ownable2Step) DropOwnershipBy(caller address) error { if !o.IsOwner(caller) { return errors.New(ErrUnauthorized) } prevOwner := o.owner o.owner = "" o.pendingOwner = "" chain.Emit( OwnershipTransferEvent, "from", prevOwner.String(), "to", "", ) return nil } // Owner returns the current owner address. Returns empty address if ownership has been dropped. // // Returns: // - address: Current owner address, or the empty address after ownership is dropped. func (o *Ownable2Step) Owner() address { return o.owner } // PendingOwner returns the pending owner address during ownership transfer. Returns empty address if no transfer is pending. // // Returns: // - address: Pending owner address, or the empty address when no transfer is pending. func (o *Ownable2Step) PendingOwner() address { return o.pendingOwner } // IsOwner returns true if the provided caller address is the current owner. // // Parameters: // - caller: Address to compare with the stored current owner address. // // Returns: // - bool: true when caller exactly equals the current owner address; false otherwise. func (o *Ownable2Step) IsOwner(caller address) bool { return o.owner == caller } // IsPendingOwner returns true if the provided caller address is the pending owner. // // Parameters: // - caller: Address to compare with the stored pending owner address. // // Returns: // - bool: true when caller exactly equals the pending owner address; false otherwise. func (o *Ownable2Step) IsPendingOwner(caller address) bool { return o.pendingOwner == caller }