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}