Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

config.gno

4.15 Kb · 98 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"
17	"chain/runtime/unsafe"
18
19	"gno.land/p/samcrew/avl"
20)
21
22// No authority address is compiled in. The publishing transaction's signer (on
23// gnoland-1 the samcrew namespace multisig, the stamped creator at enable time)
24// is seeded as admin AND treasury at package load; the DAO takes over through the
25// two-step TransferAdmin/AcceptAdmin and repoints fees with SetTreasury.
26//
27// Under `gno test` every package is initialized with an empty origin caller, and
28// this realm is read cross-realm by every engine's test suite, so an empty seed
29// would leave the engines' fee-spine tests nothing to assert against. In that
30// one case the seed becomes unseededAuthority: a sub-address of this realm that
31// no code ever mints, so no on-chain caller can ever be it, while a test can
32// impersonate it with testing.NewUserRealm. On chain the origin caller at load
33// is never empty, so this branch is unreachable there.
34
35const (
36	// MaxFeeBPS is the 5% hard ceiling. SetFeeBPS rejects anything above it, so a
37	// fat-finger or a compromised proposal can never make a lane overcharge.
38	MaxFeeBPS = int64(500)
39	// DefaultFeeBPS is returned for an unset/unknown lane so engines always get a
40	// usable value (2.0%).
41	DefaultFeeBPS = int64(200)
42)
43
44var (
45	admin        address
46	pendingAdmin address
47	treasury     address
48	feeByLane    = avl.NewTree() // lane string -> int64 bps
49)
50
51// unseededAuthority is the test-VM stand-in for a publisher; see the note above.
52var unseededAuthority = chain.DerivePkgSubAddr("gno.land/r/samcrew/memba_market_config", "unseeded")
53
54// seedAuthority makes the publisher admin and treasury. It runs once at load.
55func seedAuthority(publisher address) {
56	if publisher == "" {
57		publisher = unseededAuthority
58	}
59	admin = publisher
60	pendingAdmin = ""
61	treasury = publisher
62}
63
64func init() {
65	seedAuthority(unsafe.OriginCaller())
66	// Seed the launch lanes. "agent" is intentionally unset (TBD) and resolves to
67	// DefaultFeeBPS until the DAO sets it when that lane is built.
68	feeByLane.Set("nft", int64(200))     // 2.0%
69	feeByLane.Set("service", int64(200)) // 2.0% release fee (the 5% cancel fee is escrow-internal → freelancer)
70	feeByLane.Set("token", int64(50))    // 0.5% OTC — competitive vs DEX ~0.3%
71}
72
73func caller() address { return unsafe.PreviousRealm().Address() }
74
75// ── Read getters — PURE, NON-FAILING (never panic, no Pause) ──────────────────
76
77// GetFeeBPS returns the protocol fee in basis points for a lane. An unset or unknown
78// lane returns DefaultFeeBPS. The result is always within [0, MaxFeeBPS] (enforced on
79// write). Never panics — an engine must always be able to settle.
80func GetFeeBPS(lane string) int {
81	v, ok := feeByLane.Get(lane)
82	if !ok {
83		return int(DefaultFeeBPS)
84	}
85	return int(v.(int64))
86}
87
88// GetTreasury returns the address that receives the protocol fee on every lane.
89func GetTreasury() address { return treasury }
90
91// GetLaneConfig returns (feeBPS, treasury) in one read for an engine's settlement path.
92func GetLaneConfig(lane string) (int, address) {
93	return GetFeeBPS(lane), treasury
94}
95
96// GetAdmin returns the current admin (the multisig, or the memba_dao executor after
97// the 2-step handoff).
98func GetAdmin() address { return admin }