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

market_host.gno

3.95 Kb · 119 lines
  1package memba_weighted_host
  2
  3import (
  4	"gno.land/p/samcrew/memba_weighted_policy"
  5	"strconv"
  6)
  7
  8// MarketState is copied from the fixed target by the owning realm adapter.
  9// BPS is read only for a fee action; other actions commit zero in that field.
 10type MarketState struct {
 11	Admin        address
 12	PendingAdmin address
 13	Treasury     address
 14	BPS          int64
 15}
 16
 17type marketProposal struct {
 18	action MarketAction
 19	before MarketState
 20}
 21
 22// NewWithMarket binds immutable destinations. Keep the Host private; the realm
 23// must expose only the reviewed fixed-target adapter, never ConsumeMarket.
 24func NewWithMarket(seats []Seat, policy MarketPolicy, rlm realm) *Host {
 25	policy = NewMarketPolicy(policy.DAO(), policy.Treasury(), policy.Successor())
 26	if rlm.Address() != policy.DAO() {
 27		panic("market policy DAO does not own this host")
 28	}
 29	h := New(seats, rlm)
 30	h.marketPolicy = &policy
 31	return h
 32}
 33
 34func marketCommitment(action MarketAction, before MarketState) string {
 35	if !before.Admin.IsValid() || !before.Treasury.IsValid() || (before.PendingAdmin != "" && !before.PendingAdmin.IsValid()) || before.BPS < 0 || before.BPS > 500 {
 36		panic("invalid market pre-state")
 37	}
 38	if action.Kind() != MarketSetFee && before.BPS != 0 {
 39		panic("unexpected market fee pre-state")
 40	}
 41	return action.Bytes() + "|before|" + string(before.Admin) + "|" + string(before.PendingAdmin) + "|" + string(before.Treasury) + "|" + strconv.FormatInt(before.BPS, 10)
 42}
 43
 44func (h *Host) validateMarket(action MarketAction, before MarketState) {
 45	if h.marketPolicy == nil {
 46		panic("market actions are not configured")
 47	}
 48	marketCommitment(action, before)
 49	p := h.marketPolicy
 50	if action.Kind() == MarketAcceptAdmin {
 51		if before.Admin == p.DAO() || before.PendingAdmin != p.DAO() {
 52			panic("DAO is not pending market admin")
 53		}
 54		return
 55	}
 56	if before.Admin != p.DAO() {
 57		panic("DAO is not market admin")
 58	}
 59	if action.Kind() == MarketAbortReturn {
 60		if action.Recipient() != p.Successor() || before.PendingAdmin != p.Successor() {
 61			panic("market return does not match policy")
 62		}
 63		return
 64	}
 65	if before.PendingAdmin != "" {
 66		panic("market authority transfer is pending")
 67	}
 68	switch action.Kind() {
 69	case MarketSetFee:
 70		if action.BPS() == before.BPS {
 71			panic("market fee change is a no-op")
 72		}
 73	case MarketSetTreasury:
 74		if action.Recipient() != p.Treasury() {
 75			panic("market treasury does not match policy")
 76		}
 77		if before.Treasury == p.Treasury() {
 78			panic("market treasury change is a no-op")
 79		}
 80	case MarketReturnAdmin:
 81		if action.Recipient() != p.Successor() {
 82			panic("market successor does not match policy")
 83		}
 84	default:
 85		panic("unsupported market action")
 86	}
 87}
 88
 89func (h *Host) ProposeMarket(action MarketAction, before MarketState, rlm realm) uint64 {
 90	who := assertCaller(h, rlm)
 91	h.validateMarket(action, before)
 92	p := &roleProposal{proposer: who, market: &marketProposal{action: action, before: before}}
 93	p.ballot = h.policy.NewProposal(action.Category(), marketCommitment(action, before))
 94	h.addProposal(p)
 95	return uint64(len(h.proposals))
 96}
 97
 98// ConsumeMarket is for the owning realm's fixed adapter only. It checks the
 99// fresh target state and consumes authorization BEFORE crossing. No recovery
100// is permitted in that adapter: a target panic must abort the transaction.
101func (h *Host) ConsumeMarket(id uint64, before MarketState, rlm realm) MarketAction {
102	assertCaller(h, rlm)
103	p := h.proposal(id)
104	if p.market == nil {
105		panic("proposal is not a market action")
106	}
107	h.validateMarket(p.market.action, before)
108	p.ballot.Consume(marketCommitment(p.market.action, before))
109	// A successful application action invalidates all other authorizations.
110	// An old approval cannot revive after a return/reaccept or intervening fee
111	// change. The realm lets dispatch failure abort this mutation too.
112	members := []memba_weighted_policy.Member{}
113	for _, seat := range h.seats {
114		members = append(members, seat.Member)
115	}
116	h.policy.Reconfigure(members)
117	h.recordInvalidation(InvalidatedByExecution, id, MarketConfigPath)
118	return p.market.action
119}