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 asrlm.Previous().Address(), which must equal the current owner. The principal is therefore unforgeable: an attacker cannot supply an arbitrary caller address. Pass0as the placeholder first arg and your owncurasrlm. - Read helpers (
OwnedBy,AssertOwnedBy) take a bare address; the caller extracts it, guarding withcur.IsCurrent()before readingcur.Previous().Address(). TransferOwnershiprejects an invalidnewOwnerwithErrInvalidAddress. Both mutators emitOwnershipTransferEventwithfromandtofields.DropOwnershipis permanent:ownerbecomes"", so every owner-gated action becomes unreachable.