feat(governance): wallet_drep_vote_cast + pallas voting_procedures patch
Phase 6, key-credentialed slice (script-DRep bridge for the DAO is the remaining sub-arc). ## pallas-fork patch (Sulkta-Coop/pallas feat-aux-data HEAD 507fd9da) Threads voting_procedures through StagingTransaction → conway:: build_conway_raw, mirroring the auxiliary_data + certificates patches. - pallas-txbuilder/src/transaction/model.rs: voting_procedures field + builder methods .voting_procedures() / .clear_voting_procedures() - pallas-txbuilder/src/conway.rs: VotingProcedures::decode_fragment on the way out, assigned to TransactionBody.voting_procedures - BRANCH-NOTES.md: section 3 added documenting the new patch - 2 new tests (round-trip + negative path) on the txbuilder side aldabra Cargo.lock SHAs bumped to the new HEAD. ## aldabra-core/src/governance.rs - VoteChoice enum (Yes/No/Abstain) with into_pallas() conversion - build_signed_drep_vote_cast — assembles VotingProcedures CBOR (NonEmptyKeyValuePairs<Voter, NonEmptyKeyValuePairs<GovActionId, VotingProcedure>>) with this wallet's stake credential as a Voter::DRepKey, attaches via the new pallas API, dual-witness signs. - Optional CIP-100 anchor on the vote. ## aldabra-mcp/src/tools.rs - wallet_drep_vote_cast tool: gov_action_tx_hash + gov_action_index + vote (yes/no/abstain) + optional anchor. What's still scope-of-Phase-6: - Script-credentialed DRep voting (the DAO governor as DRep, with redeemer-driven authorization). Needs a different signing path since the voter is a script credential, not a key credential. Separate builder; defer until Sulkta wants to actually bridge.
This commit is contained in:
parent
77d4b2d0b5
commit
eb48a5cf3e
4 changed files with 279 additions and 10 deletions
|
|
@ -387,6 +387,23 @@ pub struct DrepDeregisterArgs {
|
|||
// No args — uses the wallet's stake credential.
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
pub struct DrepVoteCastArgs {
|
||||
/// Conway governance action's tx hash (64-char hex).
|
||||
pub gov_action_tx_hash: String,
|
||||
/// The action_index inside that tx (typically 0).
|
||||
pub gov_action_index: u32,
|
||||
/// One of "yes", "no", "abstain". Case-insensitive.
|
||||
pub vote: String,
|
||||
/// Optional CIP-100 anchor URL (off-chain rationale for the vote).
|
||||
#[serde(default)]
|
||||
pub anchor_url: Option<String>,
|
||||
/// 64-char hex blake2b-256 of the anchor content. Required when
|
||||
/// anchor_url is set.
|
||||
#[serde(default)]
|
||||
pub anchor_data_hash_hex: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema)]
|
||||
pub struct TxSummaryArgs {
|
||||
/// Hex-encoded Conway-era tx CBOR — unsigned, partially-signed,
|
||||
|
|
@ -1235,6 +1252,67 @@ impl WalletService {
|
|||
Ok(tx_hash)
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "wallet_drep_vote_cast",
|
||||
description = "Conway: cast this wallet's DRep vote on a governance action. Args: gov_action_tx_hash (hex), gov_action_index (u32, typically 0), vote ('yes' | 'no' | 'abstain'), anchor_url (optional CIP-100 rationale URL), anchor_data_hash_hex (optional 64-char blake2b-256 hash; required if anchor_url set). The wallet's stake credential must already be registered as a DRep for the vote to count on chain. Returns submitted tx hash."
|
||||
)]
|
||||
async fn wallet_drep_vote_cast(
|
||||
&self,
|
||||
#[tool(aggr)] DrepVoteCastArgs {
|
||||
gov_action_tx_hash,
|
||||
gov_action_index,
|
||||
vote,
|
||||
anchor_url,
|
||||
anchor_data_hash_hex,
|
||||
}: DrepVoteCastArgs,
|
||||
) -> Result<String, String> {
|
||||
let vote_choice = match vote.to_ascii_lowercase().as_str() {
|
||||
"yes" => aldabra_core::VoteChoice::Yes,
|
||||
"no" => aldabra_core::VoteChoice::No,
|
||||
"abstain" => aldabra_core::VoteChoice::Abstain,
|
||||
other => return Err(format!("vote must be yes/no/abstain, got {other:?}")),
|
||||
};
|
||||
let utxos = self
|
||||
.inner
|
||||
.chain
|
||||
.get_utxos(&self.inner.address)
|
||||
.await
|
||||
.map_err(|e| format!("fetch utxos: {e}"))?;
|
||||
if utxos.is_empty() {
|
||||
return Err(format!("no utxos at wallet address {}", self.inner.address));
|
||||
}
|
||||
let inputs: Vec<InputUtxo> = utxos
|
||||
.into_iter()
|
||||
.map(|u| InputUtxo {
|
||||
tx_hash_hex: u.tx_hash,
|
||||
output_index: u.output_index,
|
||||
lovelace: u.lovelace,
|
||||
assets: u.assets,
|
||||
})
|
||||
.collect();
|
||||
let cbor = aldabra_core::governance::build_signed_drep_vote_cast(
|
||||
&self.inner.payment_key,
|
||||
&self.inner.stake_key,
|
||||
self.inner.network,
|
||||
&inputs,
|
||||
&self.inner.address,
|
||||
&gov_action_tx_hash,
|
||||
gov_action_index,
|
||||
vote_choice,
|
||||
anchor_url.as_deref(),
|
||||
anchor_data_hash_hex.as_deref(),
|
||||
&ProtocolParams::default(),
|
||||
)
|
||||
.map_err(|e| format!("build/sign vote cast: {e}"))?;
|
||||
let tx_hash = self
|
||||
.inner
|
||||
.chain
|
||||
.submit_tx(&cbor)
|
||||
.await
|
||||
.map_err(|e| format!("submit: {e}"))?;
|
||||
Ok(tx_hash)
|
||||
}
|
||||
|
||||
#[tool(
|
||||
name = "wallet_mint_unsigned",
|
||||
description = "Build a mint TX without signing — for cold-sign or multi-sig flows. Args: dest_address, dest_lovelace, asset_name_hex, quantity, policy (optional, defaults to wallet single-sig; pass {type:'nofk',n:2,signer_pkhs_hex:[..]} for multi-sig treasury), metadata (optional CIP-25), disclosed_signer_pkh_hex (optional, defaults to wallet's pkh). Returns JSON {cbor_hex, summary}. Pass through wallet_sign_partial chain, then wallet_submit_signed_tx."
|
||||
|
|
@ -2990,7 +3068,7 @@ impl ServerHandler for WalletService {
|
|||
ServerInfo {
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
instructions: Some(
|
||||
"aldabra — Cardano lite wallet + DAO client over MCP. wallet_*: read (address/balance/utxos/network/stake_address), send (wallet_send with optional inline datum for script locks, wallet_send_unsigned + wallet_sign_partial + wallet_submit_signed_tx for cold/multi-sig, wallet_tx_status), mint (wallet_policy_create, wallet_mint with CIP-25 metadata, wallet_mint_cip68_nft for ref+user NFT pairs, wallet_mint_unsigned), Plutus (wallet_script_spend), stake (wallet_stake_delegate), Conway governance (wallet_vote_delegate to a DRep, wallet_drep_register / wallet_drep_deregister for becoming a DRep). chain_*: read-only Koios passthroughs (chain_tx_info, chain_address_info, chain_pool_list, chain_pool_info, chain_epoch_params, chain_asset_info, chain_account_info, chain_tip). dao_*: native Agora-on-Cardano DAO client. Multi-DAO via $ALDABRA_DATA/daos/<name>.json — register multiple DAOs (Sulkta, Bob's, Alice's), switch active with dao_use. Management: dao_register, dao_list, dao_use, dao_remove, dao_show. Live reads: dao_governor_state (thresholds + timing + nextProposalId), dao_stake_list (all stakes, filtered to the DAO's gov token), dao_my_stake (just this wallet's stake by pkh match). Write paths (unsigned-first; caller signs+submits): dao_proposal_create_unsigned (mint a new proposal), dao_proposal_cosign_unsigned (add wallet's stake as a Draft cosigner — multi-stake bridge), dao_proposal_vote_unsigned (vote on a VotingReady proposal), dao_proposal_advance_unsigned (state-machine push: Draft→VotingReady→Locked→Finished), dao_stake_destroy_unsigned (burn StakeST + return TRP). Each write tool pre-flights every Plutarch validator check client-side so failed txs don't burn fees.".into(),
|
||||
"aldabra — Cardano lite wallet + DAO client over MCP. wallet_*: read (address/balance/utxos/network/stake_address), send (wallet_send with optional inline datum for script locks, wallet_send_unsigned + wallet_sign_partial + wallet_submit_signed_tx for cold/multi-sig, wallet_tx_status), mint (wallet_policy_create, wallet_mint with CIP-25 metadata, wallet_mint_cip68_nft for ref+user NFT pairs, wallet_mint_unsigned), Plutus (wallet_script_spend), stake (wallet_stake_delegate), Conway governance (wallet_vote_delegate to a DRep, wallet_drep_register / wallet_drep_deregister for becoming a DRep, wallet_drep_vote_cast for casting Yes/No/Abstain votes on governance actions). chain_*: read-only Koios passthroughs (chain_tx_info, chain_address_info, chain_pool_list, chain_pool_info, chain_epoch_params, chain_asset_info, chain_account_info, chain_tip). dao_*: native Agora-on-Cardano DAO client. Multi-DAO via $ALDABRA_DATA/daos/<name>.json — register multiple DAOs (Sulkta, Bob's, Alice's), switch active with dao_use. Management: dao_register, dao_list, dao_use, dao_remove, dao_show. Live reads: dao_governor_state (thresholds + timing + nextProposalId), dao_stake_list (all stakes, filtered to the DAO's gov token), dao_my_stake (just this wallet's stake by pkh match). Write paths (unsigned-first; caller signs+submits): dao_proposal_create_unsigned (mint a new proposal), dao_proposal_cosign_unsigned (add wallet's stake as a Draft cosigner — multi-stake bridge), dao_proposal_vote_unsigned (vote on a VotingReady proposal), dao_proposal_advance_unsigned (state-machine push: Draft→VotingReady→Locked→Finished), dao_stake_destroy_unsigned (burn StakeST + return TRP). Each write tool pre-flights every Plutarch validator check client-side so failed txs don't burn fees.".into(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue