config.gno
3.52 Kb · 80 lines
1// Package memba_market_config is the DAO-owned fee spine for the Memba marketplace.
2// It is the single source of truth for the per-lane protocol fee (basis points) and
3// the treasury that receives it. Every trade engine (NFT, services, token-OTC, agents)
4// reads GetFeeBPS(lane) + GetTreasury() at settlement, so the DAO sets the rate ONCE
5// and it applies everywhere — that shared read is what makes the lanes one marketplace.
6//
7// SAFETY (panel finding C1): the read getters are PURE and NON-FAILING — they never
8// panic, and there is intentionally NO Pause(). A per-trade cross-realm read that
9// could fail or halt would be a single point of failure able to brick every engine at
10// once; instead an engine always gets a usable, bounded value here and clamps locally,
11// while each engine keeps its OWN Pause() as the real kill switch. The fee is bounded
12// to [0, MaxFeeBPS] on write, so a reader can trust the bound without re-checking.
13package memba_market_config
14
15import (
16 "chain/runtime/unsafe"
17
18 "gno.land/p/nt/avl/v0"
19)
20
21// AdminAddress is the samcrew-core 2-of-2 multisig (samcrew-core-test1) — the DAO
22// treasury + admin until the memba_dao executor handoff (via TransferAdmin/AcceptAdmin).
23// CONFIRMED 2026-06-27. Note: this is INTENTIONALLY the real 2-of-2 multisig, which
24// differs from the single-key `g1x7k4628…` hardcoded as admin in the legacy realms
25// (memba_collections/v3.1/escrow_v2) — that single-key admin is a pre-existing
26// governance gap; the fee spine is owned by the actual multisig from day one.
27const AdminAddress = "g10kw7e55e9wc8j8v6904ck29dqwr9fm9u280juh"
28
29const (
30 // MaxFeeBPS is the 5% hard ceiling. SetFeeBPS rejects anything above it, so a
31 // fat-finger or a compromised proposal can never make a lane overcharge.
32 MaxFeeBPS = int64(500)
33 // DefaultFeeBPS is returned for an unset/unknown lane so engines always get a
34 // usable value (2.0%).
35 DefaultFeeBPS = int64(200)
36)
37
38var (
39 admin address
40 pendingAdmin address
41 treasury address
42 feeByLane = avl.NewTree() // lane string -> int64 bps
43)
44
45func init() {
46 admin = address(AdminAddress)
47 treasury = address(AdminAddress) // treasury = the multisig now (plan decision #1)
48 // Seed the launch lanes. "agent" is intentionally unset (TBD) and resolves to
49 // DefaultFeeBPS until the DAO sets it when that lane is built.
50 feeByLane.Set("nft", int64(200)) // 2.0%
51 feeByLane.Set("service", int64(200)) // 2.0% release fee (the 5% cancel fee is escrow-internal → freelancer)
52 feeByLane.Set("token", int64(50)) // 0.5% OTC — competitive vs DEX ~0.3%
53}
54
55func caller() address { return unsafe.PreviousRealm().Address() }
56
57// ── Read getters — PURE, NON-FAILING (never panic, no Pause) ──────────────────
58
59// GetFeeBPS returns the protocol fee in basis points for a lane. An unset or unknown
60// lane returns DefaultFeeBPS. The result is always within [0, MaxFeeBPS] (enforced on
61// write). Never panics — an engine must always be able to settle.
62func GetFeeBPS(lane string) int {
63 v, ok := feeByLane.Get(lane)
64 if !ok {
65 return int(DefaultFeeBPS)
66 }
67 return int(v.(int64))
68}
69
70// GetTreasury returns the address that receives the protocol fee on every lane.
71func GetTreasury() address { return treasury }
72
73// GetLaneConfig returns (feeBPS, treasury) in one read for an engine's settlement path.
74func GetLaneConfig(lane string) (int, address) {
75 return GetFeeBPS(lane), treasury
76}
77
78// GetAdmin returns the current admin (the multisig, or the memba_dao executor after
79// the 2-step handoff).
80func GetAdmin() address { return admin }