feat(dao): dao_discover_scripts MCP tool + Koios discovery client
New `aldabra-dao::discovery` module:
- `DiscoveryClient` trait + `KoiosDiscoveryClient` impl
- `discover_scripts(cfg, client, deployers)` — auto-finds:
- governor_validator_ref + stake_validator_ref via deployer ref-script search
- stake_st_policy from any existing stake UTxO (gov-token + non-gov-token asset)
- stake_st_policy_ref via deployer search
- `apply_discovery(cfg, report)` — merges into DaoConfig (never overwrites)
- `script_hash_from_addr(bech32)` — extract 28-byte script hash from a script address
New MCP tool:
- `dao_discover_scripts { dao?, extra_deployers? }` — runs the audit logic
against any registered DAO + persists the discovered fields back to the
DaoConfig. Returns JSON with what was found + a gaps list for things
v1 can't auto-discover (proposal_addr, proposal_st_policy).
Plus 4 unit tests with stub Koios responses validating the full pipeline:
script-hash extraction, StakeST discovery from stake UTxO assets,
validator ref-utxo matching at deployer, apply_discovery merge semantics.
WalletInner now caches `koios_base` so the discovery client can be
constructed on demand without re-passing the URL through args.
This commit is contained in:
parent
bd62894aa8
commit
69f93339fd
3 changed files with 602 additions and 1 deletions
|
|
@ -35,6 +35,9 @@ use aldabra_dao::builder::proposal_create::{
|
|||
WalletUtxo as DaoWalletUtxo,
|
||||
};
|
||||
use aldabra_dao::config::{DaoConfig, DaoNetwork, DaoStore, ScriptRefs};
|
||||
use aldabra_dao::discovery::{
|
||||
apply_discovery, discover_scripts, KoiosDiscoveryClient, MAINNET_AGORA_SHARED_DEPLOYER,
|
||||
};
|
||||
use aldabra_dao::reader::{DaoReader, KoiosDaoReader};
|
||||
use aldabra_core::plutus_cost_models::PLUTUS_V3_COST_MODEL_PREPROD;
|
||||
use aldabra_core::{
|
||||
|
|
@ -90,6 +93,9 @@ struct WalletInner {
|
|||
/// Reader-only Koios client for DAO-shape queries. Reuses the
|
||||
/// koios_base; separate from `chain` so the trait surface stays clean.
|
||||
dao_reader: KoiosDaoReader,
|
||||
/// Cached Koios base url so `dao_discover_scripts` can spin up a
|
||||
/// `KoiosDiscoveryClient` on demand without a re-construction call.
|
||||
koios_base: String,
|
||||
}
|
||||
|
||||
impl WalletService {
|
||||
|
|
@ -111,7 +117,8 @@ impl WalletService {
|
|||
stake_key,
|
||||
max_send_lovelace,
|
||||
dao_store: DaoStore::new(&data_dir),
|
||||
dao_reader: KoiosDaoReader::new(koios_base),
|
||||
dao_reader: KoiosDaoReader::new(koios_base.clone()),
|
||||
koios_base,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -1503,6 +1510,56 @@ impl WalletService {
|
|||
Ok(serde_json::json!({ "dao": cfg.name, "stakes": arr }).to_string())
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "dao_discover_scripts",
|
||||
description = "Auto-populate a DAO's ScriptRefs + StakeST policy by inspecting on-chain state. v1 fills in: governor_validator_ref, stake_validator_ref, stake_st_policy, stake_st_policy_ref. proposal_addr + proposal_st_policy still require manual entry (v1 limitation). Searches the MLabs shared Agora deployer (`addr1w9gexmeunzsy...`) by default; pass extra deployer addresses if your DAO's scripts live elsewhere. Args: dao (optional), extra_deployers (optional list of bech32). Returns JSON {discovered, gaps, updated_config}."
|
||||
)]
|
||||
async fn dao_discover_scripts(
|
||||
&self,
|
||||
#[tool(aggr)] DaoDiscoverArgs {
|
||||
dao,
|
||||
extra_deployers,
|
||||
}: DaoDiscoverArgs,
|
||||
) -> Result<String, String> {
|
||||
let mut cfg = self
|
||||
.inner
|
||||
.dao_store
|
||||
.resolve(dao.as_deref())
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Build the deployer search list: MLabs's shared one + any caller-supplied.
|
||||
let extra: Vec<String> = extra_deployers.unwrap_or_default();
|
||||
let mut deployers: Vec<&str> = vec![MAINNET_AGORA_SHARED_DEPLOYER];
|
||||
deployers.extend(extra.iter().map(|s| s.as_str()));
|
||||
|
||||
// Use the same Koios base URL as the wallet's chain backend.
|
||||
let client = KoiosDiscoveryClient::new(self.inner.koios_base.clone());
|
||||
let report = discover_scripts(&cfg, &client, &deployers)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
apply_discovery(&mut cfg, &report);
|
||||
|
||||
// Persist.
|
||||
self.inner
|
||||
.dao_store
|
||||
.register(&cfg)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"dao": cfg.name,
|
||||
"discovered": {
|
||||
"stake_st_policy": report.stake_st_policy,
|
||||
"governor_validator_ref": report.governor_validator_ref,
|
||||
"stake_validator_ref": report.stake_validator_ref,
|
||||
"stake_st_policy_ref": report.stake_st_policy_ref,
|
||||
},
|
||||
"gaps": report.gaps,
|
||||
"config_after": cfg,
|
||||
})
|
||||
.to_string())
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "dao_proposal_create_unsigned",
|
||||
description = "Build (but DO NOT submit) an unsigned proposal-creation tx for the given DAO. Returns the CBOR-hex of the unsigned tx body + the new proposal_id. Currently supports InfoOnly proposals only — TreasuryWithdrawal effect path lands in Phase 4c. Caller signs via wallet_sign_partial then submits via wallet_submit_signed_tx. Args: dao (optional — defaults to active), fee_lovelace (suggested ~3_000_000 for v1; refine via koios tx_evaluate), starting_time_ms (POSIX millis to embed in ProposalDatum.starting_time; pass current chain tip's slot * 1000 + epoch start)."
|
||||
|
|
@ -1739,6 +1796,18 @@ pub struct DaoShowArgs {
|
|||
pub dao: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
pub struct DaoDiscoverArgs {
|
||||
/// Named DAO. Falls through to active if omitted.
|
||||
#[serde(default)]
|
||||
pub dao: Option<String>,
|
||||
/// Extra deployer addresses to search (bech32) on top of the
|
||||
/// default MLabs shared deployer. Useful for DAOs whose scripts
|
||||
/// were deployed to a private address.
|
||||
#[serde(default)]
|
||||
pub extra_deployers: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
pub struct DaoProposalCreateArgs {
|
||||
/// Named DAO. Falls through to active if omitted.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue