ic-query 0.97.0

Internet Computer query library for CloudEngine, NNS, SNS, ICRC, system canisters, and public network metadata
Documentation
//! Module: icrc::model::subaccount
//!
//! Responsibility: validate and normalize ICRC subaccount hex values.
//! Does not own: command parsing, account construction, or report fields.
//! Boundary: preserves absent values or produces exactly 32 validated bytes and canonical hex.

use super::IcrcError;

/// Validates and normalizes a 32-byte ICRC subaccount hex string.
pub fn normalize_subaccount_hex(value: &str) -> Result<String, IcrcError> {
    Ok(validated_subaccount_hex(value)?.to_ascii_lowercase())
}

#[cfg(feature = "icrc-host")]
pub(in crate::icrc) fn normalize_optional_subaccount_hex(
    value: Option<&str>,
) -> Result<Option<String>, IcrcError> {
    value.map(normalize_subaccount_hex).transpose()
}

#[cfg(any(test, feature = "icrc-host"))]
pub(in crate::icrc) fn subaccount_bytes_from_hex(value: &str) -> Result<[u8; 32], IcrcError> {
    Ok(crate::hex::decode_hex(validated_subaccount_hex(value)?)
        .expect("validated ASCII hexadecimal byte pairs")
        .try_into()
        .expect("validated 32-byte subaccount"))
}

fn validated_subaccount_hex(value: &str) -> Result<&str, IcrcError> {
    let value = value.trim();
    if !value.len().is_multiple_of(2) {
        return Err(IcrcError::InvalidSubaccountHex {
            reason: "hex string must contain an even number of characters".to_string(),
        });
    }
    if !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
        return Err(IcrcError::InvalidSubaccountHex {
            reason: "hex string must contain only ASCII hexadecimal digits".to_string(),
        });
    }
    let bytes = value.len() / 2;
    if bytes != 32 {
        return Err(IcrcError::InvalidSubaccountLength { bytes });
    }
    Ok(value)
}

#[cfg(test)]
mod tests;