feat(dao): Phase 4c-bis-1 + 4c-bis-2 — typed EffectsMap + GAT policy config

## Type port effects_raw → EffectsMap (4c-bis-1)

Replace ProposalDatum.effects_raw: PlutusData with typed
effects: EffectsMap. Required precondition for the upcoming
proposal_mint_gats builder — every downstream piece reads typed
effects, not opaque bytes.

- ProposalEffectMetadata { datum_hash: 32 bytes, script_hash: Option<28 bytes> }
  ProductIsData → CBOR Array. Field order matches Agora.Proposal.hs:296.
  Maybe ScriptHash → Constr 0 [bytes]/Constr 1 [].
- EffectsMap: Vec<(ResultTag i64, Vec<(ScriptHash, ProposalEffectMetadata)>)>.
  Encoded as nested PlutusData::Map. Keys preserved in insertion
  order (Plutus map equality is set-like, but validator builds the
  expected datum in same order so we stay consistent).
- EffectsMap::info_only(&[0, 1]) helper for the proposal_create case
  (every result_tag → empty inner map). Replaces the hand-rolled
  PlutusData::Map.
- has_neutral_effect() + keys() helpers for validator preflight checks.

Live-decode test (decodes_sulkta_live_proposal_zero) tightened:
Sulkta #0 is NOT pure InfoOnly — tag 1 has a real effect targeting
script hash 92b7..96f with datum_hash 046dff..e83c (no auth-script
wrapper). Tag 0 is empty so phasNeutralEffect still passes. Test
asserts the full typed shape now + round-trips via decode↔encode.

All 4 builders' fixtures updated: effects_raw: constr(0, vec![]) →
effects: EffectsMap::info_only(&[0, 1]). Unused constr/PlutusData/
KeyValuePairs imports pruned.

## DaoConfig GAT policy fields (4c-bis-2)

- DaoConfig.gat_policy: Option<String> (56 hex)
- ScriptRefs.gat_policy_ref: Option<String> (txhash#index)
- DaoConfig::validate now checks gat_policy + stake_st_policy +
  proposal_st_policy are 56 hex chars when set
- All DaoConfig fixtures updated with gat_policy: None
- DaoRegisterArgs gains gat_policy + gat_policy_ref optional fields
- dao_show output includes the new fields automatically (serde)

Sulkta-specific note: gat_policy hash isn't observable on chain
yet (no MintGATs tx has fired). Hand-populate from MLabs deployment
record when ready, or compute from the deployed governor's CBOR
parameters.
This commit is contained in:
Sulkta 2026-05-06 10:13:11 -07:00
parent 6406a170b0
commit d8dca8d119
9 changed files with 285 additions and 41 deletions

View file

@ -1606,11 +1606,13 @@ impl WalletService {
proposal_addr,
stake_st_policy,
proposal_st_policy,
gat_policy,
governor_validator_ref,
stake_validator_ref,
proposal_validator_ref,
stake_st_policy_ref,
proposal_st_policy_ref,
gat_policy_ref,
}: DaoRegisterArgs,
) -> Result<String, String> {
let cfg = DaoConfig {
@ -1632,6 +1634,7 @@ impl WalletService {
proposal_addr,
stake_st_policy,
proposal_st_policy,
gat_policy,
script_refs: ScriptRefs {
governor_validator: governor_validator_ref,
stake_validator: stake_validator_ref,
@ -1639,6 +1642,7 @@ impl WalletService {
treasury_validator: None,
stake_st_policy: stake_st_policy_ref,
proposal_st_policy: proposal_st_policy_ref,
gat_policy_ref,
},
};
self.inner
@ -2890,6 +2894,11 @@ pub struct DaoRegisterArgs {
/// 56 hex chars — ProposalST minting policy id.
#[serde(default)]
pub proposal_st_policy: Option<String>,
/// 56 hex chars — Governance Authority Token (GAT) minting policy id.
/// Required for the Phase 4c-bis MintGATs path. Hand-populate from
/// the DAO's deployment params.
#[serde(default)]
pub gat_policy: Option<String>,
/// `txhash#index` reference UTxO carrying the governor validator script.
#[serde(default)]
pub governor_validator_ref: Option<String>,
@ -2905,6 +2914,9 @@ pub struct DaoRegisterArgs {
/// `txhash#index` reference UTxO carrying the ProposalST minting policy script.
#[serde(default)]
pub proposal_st_policy_ref: Option<String>,
/// `txhash#index` reference UTxO carrying the GAT minting policy script.
#[serde(default)]
pub gat_policy_ref: Option<String>,
}
#[derive(Debug, Deserialize, schemars::JsonSchema)]