diff --git a/crates/aldabra-core/src/metadata.rs b/crates/aldabra-core/src/metadata.rs index 98eab5c..4dfe1a7 100644 --- a/crates/aldabra-core/src/metadata.rs +++ b/crates/aldabra-core/src/metadata.rs @@ -171,7 +171,7 @@ pub fn build_cip25_aux_data( } fn decode_hex(s: &str) -> Result, WalletError> { - if s.len() % 2 != 0 { + if !s.len().is_multiple_of(2) { return Err(WalletError::Derivation("hex string odd length".into())); } let mut out = Vec::with_capacity(s.len() / 2); diff --git a/crates/aldabra-core/src/mint.rs b/crates/aldabra-core/src/mint.rs index fdce560..58a6985 100644 --- a/crates/aldabra-core/src/mint.rs +++ b/crates/aldabra-core/src/mint.rs @@ -181,7 +181,7 @@ fn parse_tx_hash(hex_str: &str) -> Result, WalletError> { } fn parse_asset_name(hex_str: &str) -> Result, WalletError> { - if hex_str.len() % 2 != 0 { + if !hex_str.len().is_multiple_of(2) { return Err(WalletError::Derivation( "asset_name hex must have even length".into(), )); @@ -259,7 +259,7 @@ fn prepare_mint( .ok_or_else(|| WalletError::Derivation("amount overflow".into()))?; let mut sorted: Vec = available_utxos.to_vec(); - sorted.sort_by(|a, b| b.lovelace.cmp(&a.lovelace)); + sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace)); let mut acc: u64 = 0; let mut selected: Vec = Vec::new(); for u in sorted { @@ -703,7 +703,7 @@ pub fn build_signed_cip68_nft_mint( .ok_or_else(|| WalletError::Derivation("amount overflow".into()))?; let mut sorted: Vec = available_utxos.to_vec(); - sorted.sort_by(|a, b| b.lovelace.cmp(&a.lovelace)); + sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace)); let mut acc: u64 = 0; let mut selected: Vec = Vec::new(); for u in sorted { diff --git a/crates/aldabra-core/src/plutus.rs b/crates/aldabra-core/src/plutus.rs index 52be470..d698e0d 100644 --- a/crates/aldabra-core/src/plutus.rs +++ b/crates/aldabra-core/src/plutus.rs @@ -145,7 +145,7 @@ pub fn build_signed_plutus_spend( .filter(|u| u.assets.is_empty()) .cloned() .collect(); - ada_only.sort_by(|a, b| b.lovelace.cmp(&a.lovelace)); + ada_only.sort_by_key(|u| std::cmp::Reverse(u.lovelace)); // Pick collateral — largest ADA-only UTXO ≥ 5 ADA. let collateral = ada_only @@ -359,7 +359,11 @@ mod tests { const UNIT_REDEEMER_CBOR: [u8; 3] = [0xd8, 0x79, 0x80]; #[test] + #[allow(clippy::assertions_on_constants)] fn ex_units_default_is_generous() { + // Tautological today (DEFAULT_EX_UNITS is a const), but the + // intent is to guard against a future shrink that would make + // trivial validators fail to validate. Keep the check. assert!(DEFAULT_EX_UNITS.mem >= 1_000_000); assert!(DEFAULT_EX_UNITS.steps >= 100_000_000); } diff --git a/crates/aldabra-core/src/stake.rs b/crates/aldabra-core/src/stake.rs index b868068..0b11599 100644 --- a/crates/aldabra-core/src/stake.rs +++ b/crates/aldabra-core/src/stake.rs @@ -24,9 +24,9 @@ use crate::{Network, PaymentKey, ProtocolParams, StakeKey, WalletError}; /// Shelley). Refunded on deregistration. pub const STAKE_KEY_DEPOSIT_LOVELACE: u64 = 2_000_000; -const WITNESS_OVERHEAD_BYTES: u64 = 128; /// Two witnesses (payment + stake) instead of just one — used for fee -/// estimation when both keys sign. +/// estimation. Both `register_first` paths sign with both keys, so the +/// witness overhead is constant. const TWO_WITNESS_OVERHEAD_BYTES: u64 = 256; /// Decode a `pool1...` bech32 pool ID into a 28-byte Hash. @@ -84,6 +84,7 @@ fn network_id_for(network: Network) -> u8 { /// /// The tx is signed by both the payment key (body witness) and the /// stake key (cert witness). Returned CBOR is ready for submission. +#[allow(clippy::too_many_arguments)] pub fn build_signed_stake_delegation( payment_key: &PaymentKey, stake_key: &StakeKey, @@ -129,7 +130,7 @@ pub fn build_signed_stake_delegation( .ok_or_else(|| WalletError::Derivation("amount overflow".into()))?; let mut sorted: Vec = available_utxos.to_vec(); - sorted.sort_by(|a, b| b.lovelace.cmp(&a.lovelace)); + sorted.sort_by_key(|u| std::cmp::Reverse(u.lovelace)); let mut acc: u64 = 0; let mut selected: Vec = Vec::new(); for u in sorted { @@ -210,15 +211,9 @@ pub fn build_signed_stake_delegation( .map_err(|e| WalletError::Derivation(format!("conway build (pass1): {e}")))? .tx_bytes .0; - // Two witnesses for delegation: payment + stake. Fee estimate - // accounts for both. - let witness_overhead = if register_first { - TWO_WITNESS_OVERHEAD_BYTES - } else { - TWO_WITNESS_OVERHEAD_BYTES - }; - let _ = WITNESS_OVERHEAD_BYTES; // silence unused if both branches use the two-witness constant - let est_signed = (unsigned.len() as u64) + witness_overhead; + // Both delegation cases sign with two witnesses (payment + stake); + // registration doesn't add a third. + let est_signed = (unsigned.len() as u64) + TWO_WITNESS_OVERHEAD_BYTES; let real_fee = params.min_fee_for_size(est_signed); let token_change = !input_assets.is_empty(); diff --git a/crates/aldabra-core/src/tx.rs b/crates/aldabra-core/src/tx.rs index 68cc52d..acd9aae 100644 --- a/crates/aldabra-core/src/tx.rs +++ b/crates/aldabra-core/src/tx.rs @@ -191,7 +191,7 @@ fn parse_policy_id(hex_str: &str) -> Result, Walle } fn parse_asset_name(hex_str: &str) -> Result, WalletError> { - if hex_str.len() % 2 != 0 { + if !hex_str.len().is_multiple_of(2) { return Err(WalletError::Derivation( "asset_name hex must have even length".into(), )); @@ -364,6 +364,7 @@ fn output_with_assets( Ok(out) } +#[allow(clippy::too_many_arguments)] fn build_staging_with_fee( inputs: &[InputUtxo], to_addr: &PallasAddress, @@ -632,7 +633,7 @@ fn hex_encode(bytes: &[u8]) -> String { /// Hex-decode a string into a Vec — used by the cold-sign flow to /// turn the externally-signed tx hex back into bytes for submission. pub fn hex_decode(s: &str) -> Result, WalletError> { - if s.len() % 2 != 0 { + if !s.len().is_multiple_of(2) { return Err(WalletError::Derivation("hex string has odd length".into())); } let mut out = Vec::with_capacity(s.len() / 2); @@ -671,6 +672,7 @@ pub fn build_signed_payment( /// Build + sign a Conway-era payment that may include native assets. /// Returns the signed CBOR bytes ready for `ChainBackend::submit_tx`. +#[allow(clippy::too_many_arguments)] pub fn build_signed_payment_with_assets( payment_key: &PaymentKey, network: Network, @@ -722,6 +724,7 @@ pub fn build_unsigned_payment( /// signing. Returns the unsigned CBOR + a `PaymentSummary` for human /// review. Caller signs externally and submits via /// `ChainBackend::submit_tx`. +#[allow(clippy::too_many_arguments)] pub fn build_unsigned_payment_with_assets( network: Network, available_utxos: &[InputUtxo], @@ -887,7 +890,7 @@ mod tests { #[test] fn parse_asset_name_validates_length() { assert!(parse_asset_name(&"ab".repeat(33)).is_err()); // > 32 bytes - assert!(parse_asset_name(&"deadbeef").is_ok()); // 4 bytes + assert!(parse_asset_name("deadbeef").is_ok()); // 4 bytes assert!(parse_asset_name("").is_ok()); // 0 bytes — valid (ADA-symbol-like) } diff --git a/crates/aldabra-mcp/src/main.rs b/crates/aldabra-mcp/src/main.rs index 5999ada..bc50edf 100644 --- a/crates/aldabra-mcp/src/main.rs +++ b/crates/aldabra-mcp/src/main.rs @@ -82,9 +82,10 @@ async fn run() -> Result<()> { let (payment_key, stake_key, address) = { let root = if bootstrap_new { bootstrap::generate_and_save_root_key(&cfg.data_dir)? - } else if mnemonic_path.exists() { - bootstrap::load_or_create_root_key(&cfg.data_dir)? - } else if bootstrap_only { + } else if mnemonic_path.exists() || bootstrap_only { + // Both branches load (or create on the bootstrap path). + // `load_or_create_root_key` itself decides which based on + // whether `mnemonic.age` exists. bootstrap::load_or_create_root_key(&cfg.data_dir)? } else { anyhow::bail!( diff --git a/crates/aldabra-mcp/src/tools.rs b/crates/aldabra-mcp/src/tools.rs index 8b90047..e04f2d5 100644 --- a/crates/aldabra-mcp/src/tools.rs +++ b/crates/aldabra-mcp/src/tools.rs @@ -826,8 +826,10 @@ impl WalletService { // verify script_data_hash. Hardcoded preprod value from // koios epoch_params; future improvement is pulling fresh // from the chain on each call. (PLUTUS-4 audit fix.) - let mut params = ProtocolParams::default(); - params.plutus_v3_cost_model = Some(PLUTUS_V3_COST_MODEL_PREPROD.to_vec()); + let params = ProtocolParams { + plutus_v3_cost_model: Some(PLUTUS_V3_COST_MODEL_PREPROD.to_vec()), + ..ProtocolParams::default() + }; let cbor = build_signed_plutus_spend( &self.inner.payment_key,