feat(dao-mcp): wire dao_proposal_create_unsigned to fetch C-2 inputs from chain
This commit is contained in:
parent
afd0cfb298
commit
893e3f23da
1 changed files with 116 additions and 17 deletions
|
|
@ -30,8 +30,9 @@ use std::sync::Arc;
|
|||
|
||||
use aldabra_chain::{ChainBackend, KoiosClient};
|
||||
use aldabra_dao::agora::stake::Credential as DaoCredential;
|
||||
use aldabra_dao::agora::stake::StakeDatum;
|
||||
use aldabra_dao::builder::proposal_create::{
|
||||
build_unsigned_proposal_create, GovernorUtxoIn, ProposalCreateArgs, ReferenceUtxo,
|
||||
build_unsigned_proposal_create, GovernorUtxoIn, ProposalCreateArgs, ReferenceUtxo, StakeUtxoIn,
|
||||
WalletUtxo as DaoWalletUtxo,
|
||||
};
|
||||
use aldabra_dao::config::{DaoConfig, DaoNetwork, DaoStore, ScriptRefs};
|
||||
|
|
@ -1588,10 +1589,8 @@ impl WalletService {
|
|||
.map_err(|e| e.to_string())?;
|
||||
let (gov_tx_hash, gov_idx) = parse_utxo_ref(&governor_utxo_ref)?;
|
||||
|
||||
// Pull governor lovelace + every wallet UTxO. We need both:
|
||||
// (a) governor lovelace for tx-balance calculation,
|
||||
// (b) wallet utxos for funding + collateral selection.
|
||||
let gov_lovelace = self
|
||||
// Pull governor lovelace + GST asset id from the same utxo.
|
||||
let governor_utxo = self
|
||||
.inner
|
||||
.chain
|
||||
.get_utxos(&cfg.governor_addr)
|
||||
|
|
@ -1599,8 +1598,93 @@ impl WalletService {
|
|||
.map_err(|e| format!("koios get governor utxos: {e}"))?
|
||||
.into_iter()
|
||||
.find(|u| u.tx_hash == gov_tx_hash && u.output_index == gov_idx)
|
||||
.ok_or_else(|| format!("governor utxo {governor_utxo_ref} no longer present on chain"))?
|
||||
.lovelace;
|
||||
.ok_or_else(|| format!("governor utxo {governor_utxo_ref} no longer present on chain"))?;
|
||||
let gov_lovelace = governor_utxo.lovelace;
|
||||
// Extract GST policy + name from the governor utxo's asset_list.
|
||||
// Sulkta's GST has empty asset name; one asset on the utxo (qty=1) IS the GST.
|
||||
let (gst_policy_hex, gst_asset_name_hex) = governor_utxo
|
||||
.assets
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(k, _)| {
|
||||
if k.len() < 56 {
|
||||
return ("".to_string(), "".to_string());
|
||||
}
|
||||
let (p, n) = k.split_at(56);
|
||||
(p.to_string(), n.to_string())
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
"governor UTxO has no GST asset — chain state inconsistent".to_string()
|
||||
})?;
|
||||
if gst_policy_hex.is_empty() {
|
||||
return Err("governor UTxO asset key malformed (< 56 chars)".into());
|
||||
}
|
||||
|
||||
// Find the proposer's stake at stakes_addr via dao_reader.list_stakes.
|
||||
// Match on owner pkh.
|
||||
let proposer_pkh = self.wallet_pkh()?;
|
||||
let stakes = self
|
||||
.inner
|
||||
.dao_reader
|
||||
.list_stakes(&cfg)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let my_stake = stakes
|
||||
.into_iter()
|
||||
.find(|s| match &s.datum.owner {
|
||||
aldabra_dao::agora::stake::Credential::PubKey(h) => h == &proposer_pkh,
|
||||
_ => false,
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"no stake at stakes_addr owned by this wallet's pkh {} — \
|
||||
proposer must have a registered stake first",
|
||||
hex::encode(&proposer_pkh)
|
||||
)
|
||||
})?;
|
||||
let (stake_tx, stake_idx) = parse_utxo_ref(&my_stake.utxo_ref)?;
|
||||
// Pull StakeST asset name from the stake utxo.
|
||||
let stake_utxos_raw = self
|
||||
.inner
|
||||
.chain
|
||||
.get_utxos(&cfg.stakes_addr)
|
||||
.await
|
||||
.map_err(|e| format!("koios get stake utxos: {e}"))?;
|
||||
let stake_utxo_raw = stake_utxos_raw
|
||||
.into_iter()
|
||||
.find(|u| u.tx_hash == stake_tx && u.output_index == stake_idx)
|
||||
.ok_or_else(|| format!("stake utxo {} no longer on chain", my_stake.utxo_ref))?;
|
||||
let stake_st_asset_name_hex = stake_utxo_raw
|
||||
.assets
|
||||
.iter()
|
||||
.find_map(|(k, _)| {
|
||||
if k.len() < 56 {
|
||||
return None;
|
||||
}
|
||||
let (p, n) = k.split_at(56);
|
||||
if p == cfg.stake_st_policy.as_deref().unwrap_or("") {
|
||||
Some(n.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| "stake UTxO is missing StakeST token".to_string())?;
|
||||
|
||||
// Chain tip slot — for tx validity range.
|
||||
let tip_resp = self
|
||||
.inner
|
||||
.chain
|
||||
.get_raw_json("tip", &[])
|
||||
.await
|
||||
.map_err(|e| format!("koios tip: {e}"))?;
|
||||
let tip: serde_json::Value =
|
||||
serde_json::from_str(&tip_resp).map_err(|e| format!("tip parse: {e}"))?;
|
||||
let tip_slot = tip
|
||||
.as_array()
|
||||
.and_then(|a| a.first())
|
||||
.and_then(|t| t.get("abs_slot"))
|
||||
.and_then(|s| s.as_u64())
|
||||
.ok_or_else(|| format!("tip response missing abs_slot: {tip_resp}"))?;
|
||||
|
||||
let wallet_utxos: Vec<DaoWalletUtxo> = {
|
||||
let raw = self
|
||||
|
|
@ -1642,17 +1726,23 @@ impl WalletService {
|
|||
};
|
||||
|
||||
// ScriptRefs must be populated before this tool can build a tx.
|
||||
// For Sulkta the values are known from the audit; user must pass
|
||||
// them in via dao_register or hand-edit the json (until
|
||||
// dao_discover_scripts ships).
|
||||
let governor_validator_ref = ReferenceUtxo::from_str(
|
||||
cfg.script_refs
|
||||
.governor_validator
|
||||
.as_deref()
|
||||
.ok_or_else(|| {
|
||||
"DaoConfig.script_refs.governor_validator missing — populate before \
|
||||
calling dao_proposal_create_unsigned"
|
||||
.to_string()
|
||||
"DaoConfig.script_refs.governor_validator missing — \
|
||||
run dao_discover_scripts first".to_string()
|
||||
})?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let stake_validator_ref = ReferenceUtxo::from_str(
|
||||
cfg.script_refs
|
||||
.stake_validator
|
||||
.as_deref()
|
||||
.ok_or_else(|| {
|
||||
"DaoConfig.script_refs.stake_validator missing — \
|
||||
run dao_discover_scripts first".to_string()
|
||||
})?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
|
@ -1661,14 +1751,11 @@ impl WalletService {
|
|||
.proposal_st_policy
|
||||
.as_deref()
|
||||
.ok_or_else(|| {
|
||||
"DaoConfig.script_refs.proposal_st_policy missing — populate first"
|
||||
.to_string()
|
||||
"DaoConfig.script_refs.proposal_st_policy missing".to_string()
|
||||
})?,
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let proposer_pkh = self.wallet_pkh()?;
|
||||
|
||||
let unsigned = build_unsigned_proposal_create(ProposalCreateArgs {
|
||||
cfg: cfg.clone(),
|
||||
governor: GovernorUtxoIn {
|
||||
|
|
@ -1676,12 +1763,24 @@ impl WalletService {
|
|||
output_index: gov_idx,
|
||||
lovelace: gov_lovelace,
|
||||
datum: governor_datum,
|
||||
gst_policy_hex,
|
||||
gst_asset_name_hex,
|
||||
},
|
||||
stake_in: StakeUtxoIn {
|
||||
tx_hash_hex: stake_tx,
|
||||
output_index: stake_idx,
|
||||
lovelace: my_stake.lovelace,
|
||||
gov_token_qty: my_stake.gov_token_quantity,
|
||||
stake_st_asset_name_hex,
|
||||
datum: my_stake.datum,
|
||||
},
|
||||
proposer_pkh,
|
||||
change_address: self.inner.address.clone(),
|
||||
wallet_utxos,
|
||||
starting_time_ms,
|
||||
tip_slot,
|
||||
governor_validator_ref,
|
||||
stake_validator_ref,
|
||||
proposal_st_policy_ref,
|
||||
fee_lovelace,
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue