Search Apps Documentation Source Content File Folder Download Copy Actions Download

memberstore.gno

5.48 Kb · 218 lines
  1package memberstore
  2
  3import (
  4	"chain/runtime"
  5	"strings"
  6
  7	"gno.land/p/demo/svg"
  8	"gno.land/p/moul/md"
  9	"gno.land/p/nt/avl/v0"
 10	"gno.land/p/nt/mux/v0"
 11	"gno.land/p/nt/ufmt/v0"
 12	"gno.land/r/gov/dao"
 13)
 14
 15var (
 16	members MembersByTier
 17	tiers   TiersByName // private to prevent external modification
 18	router  *mux.Router
 19)
 20
 21const (
 22	T1 = "T1"
 23	T2 = "T2"
 24	T3 = "T3"
 25)
 26
 27func init() {
 28	members = NewMembersByTier()
 29
 30	tiers = TiersByName{avl.NewTree()}
 31	tiers.Set(T1, Tier{
 32		InvitationPoints: 3,
 33		MinSize: func(membersByTier MembersByTier, tiersByName TiersByName) int {
 34			return 70
 35		},
 36		MaxSize: func(membersByTier MembersByTier, tiersByName TiersByName) int {
 37			return 0
 38		},
 39		BasePower: 3,
 40		PowerHandler: func(membersByTier MembersByTier, tiersByName TiersByName) float64 {
 41			return 3
 42		},
 43	})
 44
 45	tiers.Set(T2, Tier{
 46		InvitationPoints: 2,
 47		MaxSize: func(membersByTier MembersByTier, tiersByName TiersByName) int {
 48			return membersByTier.GetTierSize(T1) * 2
 49		},
 50		MinSize: func(membersByTier MembersByTier, tiersByName TiersByName) int {
 51			return membersByTier.GetTierSize(T1) / 4
 52		},
 53		BasePower: 2,
 54		PowerHandler: func(membersByTier MembersByTier, tiersByName TiersByName) float64 {
 55			t1ms := float64(membersByTier.GetTierSize(T1))
 56			t1, _ := tiersByName.GetTier(T1)
 57			t2ms := float64(membersByTier.GetTierSize(T2))
 58			t2, _ := tiersByName.GetTier(T2)
 59
 60			t1p := t1.BasePower * t1ms
 61			t2p := t2.BasePower * t2ms
 62
 63			// capped to 2/3 of tier 1
 64			t1ptreshold := t1p * (2.0 / 3.0)
 65			if t2p > t1ptreshold {
 66				return t1ptreshold / t2ms
 67			}
 68
 69			return t2.BasePower
 70		},
 71	})
 72
 73	tiers.Set(T3, Tier{
 74		InvitationPoints: 1,
 75		MaxSize: func(membersByTier MembersByTier, tiersByName TiersByName) int {
 76			return 0
 77		},
 78		MinSize: func(membersByTier MembersByTier, tiersByName TiersByName) int {
 79			return 0
 80		},
 81		BasePower: 1,
 82		PowerHandler: func(membersByTier MembersByTier, tiersByName TiersByName) float64 {
 83			t1ms := float64(membersByTier.GetTierSize(T1))
 84			t1, _ := tiersByName.GetTier(T1)
 85			t3ms := float64(membersByTier.GetTierSize(T3))
 86			t3, _ := tiersByName.GetTier(T3)
 87
 88			t1p := t1.BasePower * t1ms
 89			t3p := t3.BasePower * t3ms
 90
 91			// capped to 1/3 of tier 1
 92			t1ptreshold := t1p * (1.0 / 3.0)
 93			if t3p > t1ptreshold {
 94				return t1ptreshold / t3ms
 95			}
 96
 97			return t3.BasePower
 98		},
 99	})
100
101	initRouter()
102}
103
104// initRouter initializes the router for the memberstore.
105func initRouter() {
106	router = mux.NewRouter()
107	router.HandleFunc("", renderHome)
108	router.HandleFunc("members", renderMembers)
109	router.NotFoundHandler = renderNotFound
110}
111
112// renderHome displays the tiers data (Number of members and powers) and tiers charts.
113func renderHome(res *mux.ResponseWriter, req *mux.Request) {
114	var sb strings.Builder
115	sb.WriteString(md.Link("> Go to Members list <", "/r/gov/dao/v3/memberstore:members") + "\n")
116
117	members.Iterate("", "", func(tn string, ti interface{}) bool {
118		tree, ok := ti.(*avl.Tree)
119		if !ok {
120			return false
121		}
122
123		tier, ok := tiers.GetTier(tn)
124		if !ok {
125			return false
126		}
127
128		tp := (tier.PowerHandler(members, tiers) * float64(members.GetTierSize(tn)))
129
130		sb.WriteString(ufmt.Sprintf("- %v Tier %v contains %v members with power: %v\n", tierColoredChip(tn), tn, tree.Size(), tp))
131
132		return false
133	})
134
135	sb.WriteString("\n" + RenderCharts(members))
136	res.Write(sb.String())
137}
138
139// renderMembers displays the members list.
140func renderMembers(res *mux.ResponseWriter, req *mux.Request) {
141	path := strings.Replace(req.RawPath, "members", "", 1) // We have to clean the path
142	res.Write(RenderMembers(path, members))
143}
144
145func renderNotFound(res *mux.ResponseWriter, req *mux.Request) {
146	res.Write("# 404\n\nThat page was not found. Would you like to [**go home**?](/r/gov/dao/v3/memberstore)")
147}
148
149func tierColor(tn string) string {
150	switch tn {
151	case T1:
152		return "#329175"
153	case T2:
154		return "#21577A"
155	case T3:
156		return "#F3D3BC"
157	default:
158		return "#FFF"
159	}
160}
161
162// tierColoredChip returns a colored chip svg for the given tier name.
163func tierColoredChip(tn string) string {
164	canvas := svg.NewCanvas(16, 16)
165	canvas.Append(svg.NewRectangle(0, 0, 16, 16, tierColor(tn)))
166	return canvas.Render(tn + " colored chip")
167}
168
169func Render(path string) string {
170	var sb strings.Builder
171	sb.WriteString(md.H1("Memberstore Govdao v3"))
172	sb.WriteString(router.Render(path))
173	return sb.String()
174}
175
176// Get gets the Members store
177func Get() MembersByTier {
178	currealm := runtime.CurrentRealm().PkgPath()
179	if !dao.InAllowedDAOs(currealm) {
180		panic("this Realm is not allowed to get the Members data: " + currealm)
181	}
182
183	return members
184}
185
186// GetTier returns a tier by name. This is a read-only accessor.
187func GetTier(name string) (Tier, bool) {
188	return tiers.GetTier(name)
189}
190
191// IterateTiers iterates over all tiers in order. This is a read-only accessor.
192// The callback receives the tier name and tier data.
193// Return true from the callback to stop iteration.
194func IterateTiers(fn func(name string, tier Tier) bool) {
195	tiers.Iterate("", "", func(name string, value interface{}) bool {
196		tier, ok := value.(Tier)
197		if !ok {
198			return false
199		}
200		return fn(name, tier)
201	})
202}
203
204// setTiers replaces the tiers configuration.
205// This is internal and should only be called via governance proposal execution.
206func setTiers(newTiers TiersByName) {
207	tiers = newTiers
208}
209
210// GetTierPower calculates the effective voting power for a tier given the current members.
211// This is a safe accessor that uses the internal tiers configuration.
212func GetTierPower(tierName string, members MembersByTier) float64 {
213	tier, ok := tiers.GetTier(tierName)
214	if !ok {
215		return 0
216	}
217	return tier.PowerHandler(members, tiers)
218}