admin.gno
3.22 Kb · 133 lines
1package admin
2
3import (
4 "chain"
5 "chain/runtime/unsafe"
6
7 "gno.land/p/akkadia/beta/accesscontrol"
8)
9
10const (
11 TransferAdminEvent = "TransferAdmin"
12 AcceptAdminEvent = "AcceptAdmin"
13 CancelAdminEvent = "CancelAdmin"
14 SetExplorerURLEvent = "SetExplorerURL"
15)
16
17var (
18 admin address
19 pendingAdmin address
20 explorerURL string = "https://app.beta.akkadia.land"
21)
22
23func init() {
24 caller := unsafe.OriginCaller()
25 admin = caller
26}
27
28func GetAdmin() address {
29 return admin
30}
31
32func GetPendingAdmin() address {
33 return pendingAdmin
34}
35
36func TransferAdmin(cur realm, newAdmin address) {
37 assertTransferAdminCaller(0, cur)
38 assertValidAddress(newAdmin)
39 if pendingAdmin.IsValid() {
40 panic("pending admin transfer already exists")
41 }
42
43 pendingAdmin = newAdmin
44
45 chain.Emit(
46 TransferAdminEvent,
47 "from", admin.String(),
48 "to", newAdmin.String(),
49 )
50}
51
52func assertTransferAdminCaller(_ int, rlm realm) {
53 if !admin.IsValid() {
54 // The admin realm normally initializes admin from OriginCaller during
55 // package deployment, so a live on-chain admin should be a valid address.
56 // An empty admin is a bootstrap-only state observed by cross-package tests:
57 // dependent realm tests can import admin before a real deploy signer exists,
58 // leaving admin as address(""). In that state there is no valid user address
59 // that can satisfy direct caller authorization, so requiring AssertIsAdmin
60 // would make those tests unable to promote a real test admin at all.
61 //
62 // Keep the legacy OriginCaller check only for this invalid-admin bootstrap
63 // state. This preserves the narrow test/bootstrap path where the current
64 // placeholder admin is address(""), while ensuring that once a real admin is
65 // accepted every subsequent admin transfer is direct-caller-only. In other
66 // words, code-realm relays cannot borrow a valid admin signer's authority to
67 // change pendingAdmin; the exception exists solely to escape the empty
68 // imported-test initialization state.
69 accesscontrol.AssertCurrentRealm(0, rlm)
70 accesscontrol.AssertIsAdminOrigin(IsAdmin)
71 return
72 }
73
74 accesscontrol.AssertIsAdmin(0, rlm, IsAdmin)
75}
76
77func AcceptAdmin(cur realm) {
78 if !pendingAdmin.IsValid() {
79 panic("invalid pending admin address")
80 }
81 caller := mustGetPendingAdminUserCaller(0, cur)
82
83 prevAdmin := admin
84 admin = caller
85 pendingAdmin = address("")
86
87 chain.Emit(
88 AcceptAdminEvent,
89 "from", prevAdmin.String(),
90 "to", admin.String(),
91 )
92}
93
94func CancelAdminTransfer(cur realm) {
95 accesscontrol.AssertIsAdmin(0, cur, IsAdmin)
96 if !pendingAdmin.IsValid() {
97 panic("invalid pending admin address")
98 }
99
100 canceledAdmin := pendingAdmin
101 pendingAdmin = address("")
102
103 chain.Emit(
104 CancelAdminEvent,
105 "admin", admin.String(),
106 "pendingAdmin", canceledAdmin.String(),
107 )
108}
109
110func IsAdmin(address address) bool {
111 return address == admin
112}
113
114// SetExplorerURL sets the explorer base URL (admin only)
115func SetExplorerURL(cur realm, url string) {
116 accesscontrol.AssertIsAdmin(0, cur, IsAdmin)
117
118 normalizedURL := normalizeExplorerURL(url)
119 oldURL := explorerURL
120 explorerURL = normalizedURL
121
122 chain.Emit(
123 SetExplorerURLEvent,
124 "admin", admin.String(),
125 "oldURL", oldURL,
126 "newURL", normalizedURL,
127 )
128}
129
130// GetExplorerURL returns the explorer base URL
131func GetExplorerURL() string {
132 return explorerURL
133}