admin.gno
2.37 Kb · 87 lines
1// Admin operations — DAO-gated, with a 2-step admin handoff and a hard fee ceiling.
2// There is intentionally NO Pause() here (see config.gno SAFETY note): pausing the
3// fee spine would brick every engine at once. Per-engine Pause() is the kill switch.
4package memba_market_config
5
6import (
7 "strconv"
8
9 "chain"
10)
11
12func itoa(n int64) string { return strconv.FormatInt(n, 10) }
13
14func assertAdmin() {
15 if caller() != admin {
16 panic("admin only")
17 }
18}
19
20// SetFeeBPS sets a lane's protocol fee in basis points, clamped to [0, MaxFeeBPS].
21// A value above the 5% ceiling is rejected so a misconfiguration can never overcharge.
22func SetFeeBPS(cur realm, lane string, bps int64) {
23 assertAdmin()
24 if lane == "" {
25 panic("empty lane")
26 }
27 if bps < 0 || bps > MaxFeeBPS {
28 panic("feeBPS out of range")
29 }
30 feeByLane.Set(lane, bps)
31 chain.Emit("FeeBPSChanged", "lane", lane, "bps", itoa(bps))
32}
33
34// SetTreasury repoints where every lane's protocol fee flows.
35func SetTreasury(cur realm, addr address) {
36 assertAdmin()
37 if addr == "" {
38 panic("empty treasury")
39 }
40 treasury = addr
41 chain.Emit("TreasuryChanged", "treasury", addr.String())
42}
43
44// TransferAdmin proposes a new admin (the memba_dao executor). The 2-step handoff
45// requires the new admin to AcceptAdmin, so a transfer to a wrong or uncontrolled
46// address cannot lock the realm.
47func TransferAdmin(cur realm, newAdmin address) {
48 assertAdmin()
49 if newAdmin == "" {
50 panic("empty admin")
51 }
52 if !newAdmin.IsValid() {
53 panic("invalid address: " + newAdmin.String())
54 }
55 if newAdmin == admin {
56 panic("newAdmin is the current admin")
57 }
58 pendingAdmin = newAdmin
59 chain.Emit("AdminTransferProposed", "pending", newAdmin.String())
60}
61
62// CancelAdminTransfer withdraws a staged handoff. Admin only.
63func CancelAdminTransfer(cur realm) {
64 assertAdmin()
65 if pendingAdmin == "" {
66 panic("no pending admin")
67 }
68 cancelled := pendingAdmin
69 pendingAdmin = ""
70 chain.Emit("AdminTransferCancelled", "pending", cancelled.String())
71}
72
73// AcceptAdmin completes the handoff. Only the pending admin can call it.
74func AcceptAdmin(cur realm) {
75 if pendingAdmin == "" {
76 panic("no pending admin")
77 }
78 if caller() != pendingAdmin {
79 panic("not pending admin")
80 }
81 admin = pendingAdmin
82 pendingAdmin = ""
83 chain.Emit("AdminAccepted", "admin", admin.String())
84}
85
86// GetPendingAdmin returns the staged successor, or the empty address.
87func GetPendingAdmin() address { return pendingAdmin }