Search Apps Documentation Source Content File Folder Download Copy Actions Download

ownable.gno

3.13 Kb · 109 lines
  1package ownable
  2
  3import "chain"
  4
  5const OwnershipTransferEvent = "OwnershipTransfer"
  6
  7// Ownable is meant to be used as a top-level object to make your contract
  8// ownable OR being embedded in a Gno object to manage per-object ownership.
  9// Ownable is safe to export as a top-level object.
 10//
 11// Authority-mutating methods (TransferOwnership, DropOwnership) take
 12// (_ int, rlm realm). The caller threads its own cur; the method
 13// asserts rlm.IsCurrent() and identifies the principal as
 14// rlm.Previous().Address() — which must equal the current owner.
 15//
 16//	o.TransferOwnership(0, cur, newOwner)
 17//	o.DropOwnership(0, cur)
 18//
 19// Read methods (OwnedBy, AssertOwnedBy) keep the bare-address shape;
 20// callers extract the address themselves (e.g. cur.Previous().Address()).
 21type Ownable struct {
 22	owner address
 23}
 24
 25// NewWithAddress creates an Ownable with the given address as owner.
 26// This is the only constructor — the previous New/NewWithOrigin/
 27// NewWithAddressByPrevious sugar baked runtime walks and an auth-mode
 28// flag into the struct; the realm using this package now picks the
 29// owner address explicitly (e.g. cur.Previous().Address() after
 30// verifying cur.Previous().IsUserCall() in init).
 31func NewWithAddress(addr address) *Ownable {
 32	return &Ownable{
 33		owner: addr,
 34	}
 35}
 36
 37// OwnedBy reports whether addr is the current owner.
 38func (o *Ownable) OwnedBy(addr address) bool {
 39	if o == nil {
 40		return false
 41	}
 42	return addr == o.owner
 43}
 44
 45// AssertOwnedBy panics with ErrUnauthorized if addr is not the owner.
 46func (o *Ownable) AssertOwnedBy(addr address) {
 47	if !o.OwnedBy(addr) {
 48		panic(ErrUnauthorized)
 49	}
 50}
 51
 52// TransferOwnership transfers ownership of the Ownable to newOwner. rlm
 53// must be the caller's own captured cur (asserted via rlm.IsCurrent()).
 54// The principal is rlm.Previous().Address() — the realm that crossed
 55// into the caller — which must equal the current owner.
 56//
 57// IsCurrent + rlm.Previous() makes the principal unforgeable: an
 58// attacker calling TransferOwnership on a foreign Ownable cannot supply
 59// an arbitrary caller address; rlm comes from a runtime-validated
 60// crossing frame.
 61func (o *Ownable) TransferOwnership(_ int, rlm realm, newOwner address) error {
 62	if !rlm.IsCurrent() {
 63		return ErrUnauthorized
 64	}
 65	caller := rlm.Previous().Address()
 66	if !o.OwnedBy(caller) {
 67		return ErrUnauthorized
 68	}
 69	if !newOwner.IsValid() {
 70		return ErrInvalidAddress
 71	}
 72	prevOwner := o.owner
 73	o.owner = newOwner
 74	chain.Emit(
 75		OwnershipTransferEvent,
 76		"from", prevOwner.String(),
 77		"to", newOwner.String(),
 78	)
 79	return nil
 80}
 81
 82// DropOwnership removes the owner, disabling any owner-related actions.
 83// rlm must be the caller's own captured cur; rlm.Previous().Address()
 84// must equal the current owner.
 85func (o *Ownable) DropOwnership(_ int, rlm realm) error {
 86	if !rlm.IsCurrent() {
 87		return ErrUnauthorized
 88	}
 89	caller := rlm.Previous().Address()
 90	if !o.OwnedBy(caller) {
 91		return ErrUnauthorized
 92	}
 93	prevOwner := o.owner
 94	o.owner = ""
 95	chain.Emit(
 96		OwnershipTransferEvent,
 97		"from", prevOwner.String(),
 98		"to", "",
 99	)
100	return nil
101}
102
103// Owner returns the owner address.
104func (o *Ownable) Owner() address {
105	if o == nil {
106		return address("")
107	}
108	return o.owner
109}