config.gno
4.20 Kb · 115 lines
1// Package config is the one realm moul's other realms read their settings
2// from: a key/value store plus the manager list that decides who may write it.
3//
4// # Why a realm and not a constant
5//
6// A value shared by several contracts, hardcoded in each, is changed by
7// redeploying each. On mainnet a public realm cannot even be redeployed at its
8// own path, so "changing it" means a new version and a new path for every
9// consumer. Here it is a transaction.
10//
11// # Bump rarely, and never for a new setting
12//
13// This realm IS public and versioned, so its path moves on every bump. That
14// makes a bump expensive, which is exactly why the settings API is generic: a
15// new setting is a new KEY, and a key costs one transaction. Only a change to
16// the shape of this realm (a new function, a changed signature) is worth a
17// version.
18//
19// # When a bump does happen, the state does not move
20//
21// A later version imports this one and relays to it rather than holding
22// anything of its own, so v1, v2 and v3 all read and write the same settings,
23// the same pause and the same member list. Delegation only runs backwards in
24// time, which is why the root is the oldest version that has the state and not
25// the newest. See proxy.gno.
26//
27// # What lives here
28//
29// settings.gno the key/value store, and the mygnoscan accessors over it
30// keys.gno the key grammar: a name, optionally scoped to one realm
31// blocks.gno the notice a realm renders above and below its content
32// pause.gno the switch a realm checks before it acts
33// proxy.gno the relay that lets a later version share this state
34// config.gno the member list, unchanged from v0
35//
36// # Governance
37//
38// init seeds the authority with the deploying EOA. Everything that writes goes
39// through Authorizer, so transferring authority to a DAO transfers the
40// settings with it: there is no address hardcoded on the write path.
41package config
42
43import (
44 "errors"
45
46 "gno.land/p/moul/authz/v0"
47)
48
49var Authorizer *authz.Authorizer
50
51func init(cur realm) {
52 if !cur.Previous().IsUserCall() {
53 panic("r/moul/config must be initialized by an EOA")
54 }
55 Authorizer = authz.NewWithMembers(cur.Previous().Address())
56}
57
58// AddManager adds a new address to the list of authorized managers.
59// This only works if the current authority is a MemberAuthority.
60// The caller must be authorized by the current authority.
61func AddManager(cur realm, addr address) error {
62 memberAuth, ok := Authorizer.Authority().(*authz.MemberAuthority)
63 if !ok {
64 return errors.New("current authority is not a MemberAuthority, cannot add manager directly")
65 }
66 return memberAuth.AddMember(0, cur, addr)
67}
68
69// RemoveManager removes an address from the list of authorized managers.
70// This only works if the current authority is a MemberAuthority.
71// The caller must be authorized by the current authority.
72func RemoveManager(cur realm, addr address) error {
73 memberAuth, ok := Authorizer.Authority().(*authz.MemberAuthority)
74 if !ok {
75 return errors.New("current authority is not a MemberAuthority, cannot remove manager directly")
76 }
77 return memberAuth.RemoveMember(0, cur, addr)
78}
79
80// TransferManagement transfers the authority to manage keys to a new authority.
81// The caller must be authorized by the current authority.
82func TransferManagement(cur realm, newAuthority authz.Authority) error {
83 if newAuthority == nil {
84 return errors.New("new authority cannot be nil")
85 }
86 return Authorizer.Transfer(0, cur, newAuthority)
87}
88
89// ListManagers returns a slice of all managed keys.
90func ListManagers(cur realm) []address {
91 var keyList []address
92 memberAuth, ok := Authorizer.Authority().(*authz.MemberAuthority)
93 if !ok {
94 return keyList
95 }
96 tree := memberAuth.Tree()
97 if !ok || tree == nil {
98 return keyList // Return empty list if tree is not as expected or nil
99 }
100 tree.Iterate("", "", func(key string, _ any) bool {
101 keyList = append(keyList, address(key))
102 return false
103 })
104 return keyList
105}
106
107func HasManager(cur realm, addr address) bool {
108 memberAuth, ok := Authorizer.Authority().(*authz.MemberAuthority)
109 if !ok {
110 return false // Return false if not a MemberAuthority or doesn't exist
111 }
112 // Use the MemberAuthority's specific RemoveMember method,
113 // which internally performs the authorization check.
114 return memberAuth.Has(addr)
115}