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}