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
4d3ef03978
commit
6443dcd858
4 changed files with 279 additions and 10 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -1253,7 +1253,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
|||
[[package]]
|
||||
name = "pallas-addresses"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"base58",
|
||||
"bech32",
|
||||
|
|
@ -1268,7 +1268,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallas-codec"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"minicbor",
|
||||
|
|
@ -1279,7 +1279,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallas-crypto"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"cryptoxide",
|
||||
"hex",
|
||||
|
|
@ -1293,7 +1293,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallas-primitives"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"base58",
|
||||
"bech32",
|
||||
|
|
@ -1308,7 +1308,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallas-traverse"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"itertools",
|
||||
|
|
@ -1324,7 +1324,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallas-txbuilder"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"pallas-addresses",
|
||||
|
|
@ -1341,7 +1341,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallas-wallet"
|
||||
version = "0.32.1"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#68221fbcb57ea499502ad23547313ff91a21ba72"
|
||||
source = "git+http://kayos:***REDACTED***@192.168.0.5:3001/Sulkta-Coop/pallas.git?branch=feat-aux-data#507fd9da15f1239ff2df866e0d7601d4518e83a3"
|
||||
dependencies = [
|
||||
"bech32",
|
||||
"bip39",
|
||||
|
|
|
|||
|
|
@ -279,6 +279,197 @@ pub fn build_signed_drep_registration(
|
|||
)
|
||||
}
|
||||
|
||||
/// One Yes/No/Abstain vote on one Conway governance action.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VoteChoice {
|
||||
Yes,
|
||||
No,
|
||||
Abstain,
|
||||
}
|
||||
|
||||
impl VoteChoice {
|
||||
fn into_pallas(self) -> pallas_primitives::conway::Vote {
|
||||
use pallas_primitives::conway::Vote;
|
||||
match self {
|
||||
VoteChoice::Yes => Vote::Yes,
|
||||
VoteChoice::No => Vote::No,
|
||||
VoteChoice::Abstain => Vote::Abstain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build + sign a DRep vote-cast tx. The wallet's stake credential
|
||||
/// signs as the DRep voter — must already be registered as a DRep
|
||||
/// (via `build_signed_drep_registration` or a separate flow) for the
|
||||
/// vote to count on chain.
|
||||
///
|
||||
/// `gov_action_tx_hash_hex` + `gov_action_index` identify the Conway
|
||||
/// governance action to vote on (look these up via Koios / chain
|
||||
/// passthrough tools — `chain_governance_actions` will land alongside
|
||||
/// this when wired). `anchor_url` + `anchor_data_hash_hex` are optional
|
||||
/// per-vote rationale (CIP-100). Pass `None` for both when omitting.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_signed_drep_vote_cast(
|
||||
payment_key: &PaymentKey,
|
||||
stake_key: &StakeKey,
|
||||
network: Network,
|
||||
available_utxos: &[InputUtxo],
|
||||
change_address_bech32: &str,
|
||||
gov_action_tx_hash_hex: &str,
|
||||
gov_action_index: u32,
|
||||
vote: VoteChoice,
|
||||
anchor_url: Option<&str>,
|
||||
anchor_data_hash_hex: Option<&str>,
|
||||
params: &ProtocolParams,
|
||||
) -> Result<Vec<u8>, WalletError> {
|
||||
use pallas_codec::utils::{NonEmptyKeyValuePairs, Nullable};
|
||||
use pallas_primitives::conway::{
|
||||
Anchor, GovActionId, Voter, VotingProcedure, VotingProcedures,
|
||||
};
|
||||
|
||||
let stake_pkh = stake_key.public_key_hash();
|
||||
let voter = Voter::DRepKey(stake_pkh);
|
||||
|
||||
let gov_tx_hash = parse_tx_hash(gov_action_tx_hash_hex)?;
|
||||
let gov_action_id = GovActionId {
|
||||
transaction_id: gov_tx_hash,
|
||||
action_index: gov_action_index,
|
||||
};
|
||||
|
||||
let anchor: Nullable<Anchor> = match (anchor_url, anchor_data_hash_hex) {
|
||||
(Some(url), Some(hash_hex)) => {
|
||||
if hash_hex.len() != 64 {
|
||||
return Err(WalletError::Derivation(format!(
|
||||
"anchor_data_hash must be 64-char hex, got {}",
|
||||
hash_hex.len()
|
||||
)));
|
||||
}
|
||||
let mut h_arr = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
h_arr[i] = u8::from_str_radix(&hash_hex[i * 2..i * 2 + 2], 16).map_err(|_| {
|
||||
WalletError::Derivation("invalid hex in anchor_data_hash".into())
|
||||
})?;
|
||||
}
|
||||
Nullable::Some(Anchor {
|
||||
url: url.to_string(),
|
||||
content_hash: Hash::<32>::new(h_arr),
|
||||
})
|
||||
}
|
||||
(None, None) => Nullable::Null,
|
||||
_ => {
|
||||
return Err(WalletError::Derivation(
|
||||
"anchor_url and anchor_data_hash must both be set or both omitted".into(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let procedure = VotingProcedure {
|
||||
vote: vote.into_pallas(),
|
||||
anchor,
|
||||
};
|
||||
|
||||
let inner = NonEmptyKeyValuePairs::Def(vec![(gov_action_id, procedure)]);
|
||||
let outer: VotingProcedures = NonEmptyKeyValuePairs::Def(vec![(voter, inner)]);
|
||||
let vp_bytes = minicbor::to_vec(&outer)
|
||||
.map_err(|e| WalletError::Derivation(format!("encode voting procedures: {e}")))?;
|
||||
|
||||
sign_voting_tx(
|
||||
payment_key,
|
||||
stake_key,
|
||||
network,
|
||||
available_utxos,
|
||||
change_address_bech32,
|
||||
vp_bytes,
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shared voting-tx signing: builds a tx with `voting_procedures`
|
||||
/// attached, two-pass-fee, dual-witness (payment + stake) signed.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn sign_voting_tx(
|
||||
payment_key: &PaymentKey,
|
||||
stake_key: &StakeKey,
|
||||
network: Network,
|
||||
available_utxos: &[InputUtxo],
|
||||
change_address_bech32: &str,
|
||||
voting_procedures_cbor: Vec<u8>,
|
||||
params: &ProtocolParams,
|
||||
) -> Result<Vec<u8>, WalletError> {
|
||||
let change_addr = parse_address(change_address_bech32)?;
|
||||
let network_id = network_id_for(network);
|
||||
|
||||
let fee_pass1: u64 = 500_000;
|
||||
let need = fee_pass1
|
||||
.checked_add(params.min_utxo_lovelace)
|
||||
.ok_or_else(|| WalletError::Derivation("amount overflow".into()))?;
|
||||
|
||||
let mut sorted: Vec<InputUtxo> = available_utxos.to_vec();
|
||||
sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace));
|
||||
let mut acc: u64 = 0;
|
||||
let mut selected: Vec<InputUtxo> = Vec::new();
|
||||
for u in sorted {
|
||||
acc = acc.saturating_add(u.lovelace);
|
||||
selected.push(u);
|
||||
if acc >= need {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if acc < need {
|
||||
return Err(WalletError::Derivation(format!(
|
||||
"insufficient funds: need {need} (fee+min_change), have {acc}"
|
||||
)));
|
||||
}
|
||||
let total_in: u64 = selected.iter().map(|u| u.lovelace).sum();
|
||||
|
||||
let build_with_fee = |fee: u64, change_lovelace: u64| -> Result<StagingTransaction, WalletError> {
|
||||
let mut staging = StagingTransaction::new();
|
||||
for u in &selected {
|
||||
let h = parse_tx_hash(&u.tx_hash_hex)?;
|
||||
staging = staging.input(Input::new(h, u.output_index as u64));
|
||||
}
|
||||
let change_out = Output::new(change_addr.clone(), change_lovelace);
|
||||
staging = staging.output(change_out);
|
||||
staging = staging.voting_procedures(voting_procedures_cbor.clone());
|
||||
staging = staging.fee(fee).network_id(network_id);
|
||||
Ok(staging)
|
||||
};
|
||||
|
||||
let change_pass1 = total_in
|
||||
.checked_sub(fee_pass1)
|
||||
.ok_or_else(|| WalletError::Derivation("pass1: insufficient lovelace".into()))?;
|
||||
let staging1 = build_with_fee(fee_pass1, change_pass1)?;
|
||||
let unsigned = staging1
|
||||
.build_conway_raw()
|
||||
.map_err(|e| WalletError::Derivation(format!("conway build (pass1): {e}")))?
|
||||
.tx_bytes
|
||||
.0;
|
||||
let est_signed = (unsigned.len() as u64) + TWO_WITNESS_OVERHEAD_BYTES;
|
||||
let real_fee = params.min_fee_for_size(est_signed);
|
||||
|
||||
let final_change = total_in
|
||||
.checked_sub(real_fee)
|
||||
.ok_or_else(|| WalletError::Derivation(format!(
|
||||
"insufficient funds for fee: total_in={total_in} fee={real_fee}"
|
||||
)))?;
|
||||
if final_change < params.min_utxo_lovelace {
|
||||
return Err(WalletError::Derivation(format!(
|
||||
"change ({final_change}) below min utxo ({})",
|
||||
params.min_utxo_lovelace
|
||||
)));
|
||||
}
|
||||
|
||||
let staging2 = build_with_fee(real_fee, final_change)?;
|
||||
let built = staging2
|
||||
.build_conway_raw()
|
||||
.map_err(|e| WalletError::Derivation(format!("conway build (final): {e}")))?;
|
||||
|
||||
let payment_signed = add_witness(payment_key, &built.tx_bytes.0)?;
|
||||
let stake_payment_proxy = crate::stake::stake_key_as_payment_proxy(stake_key);
|
||||
let fully_signed = add_witness(&stake_payment_proxy, &payment_signed)?;
|
||||
Ok(fully_signed)
|
||||
}
|
||||
|
||||
/// Build + sign a DRep deregistration tx. Returns the 500 ADA deposit
|
||||
/// to the wallet.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ pub use plutus::{
|
|||
pub use stake::{build_signed_stake_delegation, parse_pool_id, STAKE_KEY_DEPOSIT_LOVELACE};
|
||||
pub use governance::{
|
||||
build_signed_drep_deregistration, build_signed_drep_registration,
|
||||
build_signed_vote_delegation, parse_drep_target, DRepTarget,
|
||||
DREP_REGISTRATION_DEPOSIT_LOVELACE,
|
||||
build_signed_drep_vote_cast, build_signed_vote_delegation, parse_drep_target,
|
||||
DRepTarget, VoteChoice, DREP_REGISTRATION_DEPOSIT_LOVELACE,
|
||||
};
|
||||
pub use tx::{
|
||||
build_signed_payment, build_signed_payment_with_assets, build_unsigned_payment,
|
||||
|
|
|
|||
|
|
@ -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