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

channels_actions.gno

5.23 Kb · 111 lines
  1package memba_weighted_host
  2
  3import (
  4	"chain"
  5	"strconv"
  6	"strings"
  7	"gno.land/p/samcrew/memba_weighted_policy"
  8)
  9
 10const ChannelsPath = "gno.land/r/samcrew/memba_dao_channels_v2"
 11
 12type ChannelsActionKind string
 13
 14const (
 15	ChannelsAcceptOwner ChannelsActionKind = "accept-owner"
 16	ChannelsReturnOwner ChannelsActionKind = "return-owner"
 17	ChannelsAbortReturn ChannelsActionKind = "abort-return"
 18	ChannelsAddMember ChannelsActionKind = "add-member"
 19	ChannelsRemoveMember ChannelsActionKind = "remove-member"
 20	ChannelsSetRoles ChannelsActionKind = "set-roles"
 21	ChannelsCreateText ChannelsActionKind = "create-text-channel"
 22	ChannelsUnpause ChannelsActionKind = "unpause"
 23)
 24
 25type ChannelsPolicy struct{ dao, successor address }
 26
 27func NewChannelsPolicy(dao, successor address) ChannelsPolicy {
 28	if !dao.IsValid() || dao != chain.PackageAddress("gno.land/r/samcrew/memba_dao") {
 29		panic("channels policy requires the founding DAO address")
 30	}
 31	if !successor.IsValid() || successor == dao || successor == chain.PackageAddress(ChannelsPath) {
 32		panic("channels policy requires a valid distinct successor")
 33	}
 34	return ChannelsPolicy{dao: dao, successor: successor}
 35}
 36func (p ChannelsPolicy) DAO() address { return p.dao }
 37func (p ChannelsPolicy) Successor() address { return p.successor }
 38
 39type ChannelsAction struct {
 40	kind ChannelsActionKind
 41	recipient, subject address
 42	roles string
 43	name, description string
 44}
 45func (p ChannelsPolicy) Accept() ChannelsAction { return ChannelsAction{kind: ChannelsAcceptOwner} }
 46func (p ChannelsPolicy) Return() ChannelsAction { return ChannelsAction{kind: ChannelsReturnOwner, recipient: p.successor} }
 47func (p ChannelsPolicy) AbortReturn() ChannelsAction { return ChannelsAction{kind: ChannelsAbortReturn, recipient: p.successor} }
 48func (p ChannelsPolicy) AddMember(who address, roles string) ChannelsAction { a:=ChannelsAction{kind: ChannelsAddMember, subject:who, roles:roles}; a.Bytes(); return a }
 49func (p ChannelsPolicy) RemoveMember(who address) ChannelsAction { a:=ChannelsAction{kind: ChannelsRemoveMember, subject:who}; a.Bytes(); return a }
 50func (p ChannelsPolicy) SetRoles(who address, roles string) ChannelsAction { a:=ChannelsAction{kind: ChannelsSetRoles, subject:who, roles:roles}; a.Bytes(); return a }
 51func (p ChannelsPolicy) CreateText(name, description string) ChannelsAction { a:=ChannelsAction{kind: ChannelsCreateText, name:name, description:description}; a.Bytes(); return a }
 52func ChannelsResume() ChannelsAction { return ChannelsAction{kind: ChannelsUnpause} }
 53func (a ChannelsAction) Kind() ChannelsActionKind { return a.kind }
 54func (a ChannelsAction) Recipient() address { return a.recipient }
 55func (a ChannelsAction) Subject() address { return a.subject }
 56func (a ChannelsAction) Roles() string { return a.roles }
 57func (a ChannelsAction) Name() string { return a.name }
 58func (a ChannelsAction) Description() string { return a.description }
 59
 60func validChannelsName(name string) bool {
 61	if len(name)<1 || len(name)>50 { return false }
 62	for _,c:=range name { if !((c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='-') { return false } }
 63	return true
 64}
 65func validChannelsDescription(description string) bool {
 66	if len(description)<1 || len(description)>200 { return false }
 67	for _,c:=range description { if c<32 || c==127 { return false } }
 68	return true
 69}
 70
 71// Only the four target ACL roles are representable; every role list has a
 72// canonical order, so a voted commitment cannot alias another role grant.
 73func canonicalChannelsRoles(roles string) bool {
 74	if roles == "" { return false }
 75	parts:=strings.Split(roles, ",")
 76	if len(parts)>4 { return false }
 77	previous:=-1
 78	for _,part:=range parts {
 79		index:=-1
 80		switch part { case "admin": index=0; case "dev": index=1; case "ops": index=2; case "member": index=3 }
 81		if index<=previous { return false }
 82		previous=index
 83	}
 84	return true
 85}
 86func (a ChannelsAction) Bytes() string {
 87	base:="channels/v1|"+ChannelsPath+"|"+string(a.kind)
 88	switch a.kind {
 89	case ChannelsAcceptOwner, ChannelsUnpause:
 90		if a.recipient!="" || a.subject!="" || a.roles!="" || a.name!="" || a.description!="" { panic("malformed channels action") }
 91		return base
 92	case ChannelsReturnOwner, ChannelsAbortReturn:
 93		if !a.recipient.IsValid() || a.subject!="" || a.roles!="" || a.name!="" || a.description!="" { panic("malformed channels return action") }
 94		return base+"|"+string(a.recipient)
 95	case ChannelsAddMember, ChannelsSetRoles:
 96		if !a.subject.IsValid() || a.recipient!="" || a.name!="" || a.description!="" || !canonicalChannelsRoles(a.roles) { panic("invalid channels member action") }
 97		return base+"|"+string(a.subject)+"|"+strconv.Itoa(len(a.roles))+":"+a.roles
 98	case ChannelsRemoveMember:
 99		if !a.subject.IsValid() || a.recipient!="" || a.roles!="" || a.name!="" || a.description!="" { panic("invalid channels member action") }
100		return base+"|"+string(a.subject)
101	case ChannelsCreateText:
102		if a.recipient!="" || a.subject!="" || a.roles!="" || !validChannelsName(a.name) || !validChannelsDescription(a.description) { panic("invalid channels text creation") }
103		return base+"|"+a.name+"|"+strconv.Itoa(len(a.description))+":"+a.description
104	default: panic("unsupported channels action")
105	}
106}
107func (a ChannelsAction) Category() memba_weighted_policy.Category {
108	a.Bytes()
109	if a.kind==ChannelsUnpause { return memba_weighted_policy.Financial }
110	return memba_weighted_policy.Critical
111}