Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

README.md

3.30 Kb · 86 lines

v0 - Unaudited This is an initial version of this package that has not yet been formally audited. A fully audited version will be published as a subsequent release. Use in production at your own risk.

ownable - Ownership pattern for realms

Provides an Ownable object that gates privileged operations behind a single owner address. Embed it in a realm (or any struct) to restrict actions like configuration changes, withdrawals, or upgrades.

Usage

 1package myrealm
 2
 3import (
 4    "chain/runtime"
 5
 6    "gno.land/p/nt/ownable/v0"
 7)
 8
 9// The owner address is chosen explicitly at construction. A common
10// choice is the deployer, captured in init after confirming it is a
11// real user call.
12var owner *ownable.Ownable
13
14func init() {
15    caller := runtime.PreviousRealm()
16    if !caller.IsUserCall() {
17        panic("must be deployed by a user")
18    }
19    owner = ownable.NewWithAddress(caller.Address())
20}
21
22// SetFee is gated: only the current owner may call it.
23func SetFee(cur realm, newFee int64) {
24    if !cur.IsCurrent() {
25        panic("spoofed realm")
26    }
27    owner.AssertOwnedBy(cur.Previous().Address())
28    fee = newFee
29}
30
31// Hand the realm over. TransferOwnership itself verifies the caller is owner.
32func TransferOwner(cur realm, to address) error {
33    return owner.TransferOwnership(0, cur, to)
34}

There is no auth-mode flag. The single NewWithAddress constructor replaced the old New / NewWithOrigin / NewWithAddressByPrevious sugar: the realm now picks the owner address explicitly rather than baking a runtime walk into the struct.

API

 1type Ownable struct{ /* unexported */ }
 2
 3const OwnershipTransferEvent = "OwnershipTransfer"
 4
 5var (
 6    ErrUnauthorized   = errors.New("ownable: caller is not owner")
 7    ErrInvalidAddress = errors.New("ownable: new owner address is invalid")
 8)
 9
10// NewWithAddress is the only constructor: the realm picks the owner
11// address explicitly (e.g. cur.Previous().Address() after checking
12// cur.Previous().IsUserCall() in init).
13func NewWithAddress(addr address) *Ownable
14
15// Queries (caller supplies the address to check).
16func (o *Ownable) Owner() address             // "" if o is nil or ownership was dropped
17func (o *Ownable) OwnedBy(addr address) bool  // true if addr is the current owner
18func (o *Ownable) AssertOwnedBy(addr address) // panics with ErrUnauthorized if addr is not the owner
19
20// Authority mutation (thread the caller's own cur; pass 0 as the first arg).
21func (o *Ownable) TransferOwnership(_ int, rlm realm, newOwner address) error
22func (o *Ownable) DropOwnership(_ int, rlm realm) error // sets owner to "" — irreversible

Notes

  • Authority-mutating methods assert rlm.IsCurrent() and identify the caller as rlm.Previous().Address(), which must equal the current owner. The principal is therefore unforgeable: an attacker cannot supply an arbitrary caller address. Pass 0 as the placeholder first arg and your own cur as rlm.
  • Read helpers (OwnedBy, AssertOwnedBy) take a bare address; the caller extracts it, guarding with cur.IsCurrent() before reading cur.Previous().Address().
  • TransferOwnership rejects an invalid newOwner with ErrInvalidAddress. Both mutators emit OwnershipTransferEvent with from and to fields.
  • DropOwnership is permanent: owner becomes "", so every owner-gated action becomes unreachable.