From ecb945f01539d70454c0a2598301fd045f986f70 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Fri, 7 Aug 2026 10:28:16 +0200 Subject: [PATCH] fix(minibf): return a JSON error body for a malformed stake address The account endpoints resolved the stake address with `parse_account_key_param`. For a malformed address, the helper returned a bare 400 with an empty body. The real Blockfrost API returns a JSON body with the message "Invalid or malformed stake address format." on every account endpoint. A blockfrost-tests run found this mismatch on `/accounts/{stake_address}/addresses/assets`. The helper now returns `Error::InvalidStakeAddress`, so all account endpoints send the correct 400 body. --- crates/minibf/src/error.rs | 10 ++++++++++ crates/minibf/src/routes/accounts.rs | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/minibf/src/error.rs b/crates/minibf/src/error.rs index 355cea554..71a3e9f03 100644 --- a/crates/minibf/src/error.rs +++ b/crates/minibf/src/error.rs @@ -12,6 +12,7 @@ pub enum Error { Pagination(PaginationError), Code(StatusCode), InvalidAddress, + InvalidStakeAddress, InvalidAsset, InvalidPoolId, InvalidBlockNumber, @@ -63,6 +64,15 @@ impl IntoResponse for Error { )), ) .into_response(), + Error::InvalidStakeAddress => ( + StatusCode::BAD_REQUEST, + Json(ErrorBody::new( + 400, + "Bad Request", + "Invalid or malformed stake address format.", + )), + ) + .into_response(), Error::InvalidAsset => ( StatusCode::BAD_REQUEST, Json(ErrorBody::new( diff --git a/crates/minibf/src/routes/accounts.rs b/crates/minibf/src/routes/accounts.rs index 0b079af4c..fff0281da 100644 --- a/crates/minibf/src/routes/accounts.rs +++ b/crates/minibf/src/routes/accounts.rs @@ -83,13 +83,13 @@ fn stake_address_from_cip_19_credential(address: &str, network: Network) -> Opti Some(StakeAddress::new(network, payload)) } -fn parse_account_key_param(address: &str, network: Network) -> Result { +fn parse_account_key_param(address: &str, network: Network) -> Result { let address = if let Some(stake_address) = stake_address_from_cip_19_credential(address, network) { stake_address } else { let parsed = pallas::ledger::addresses::Address::from_bech32(address) - .map_err(|_| StatusCode::BAD_REQUEST)?; + .map_err(|_| Error::InvalidStakeAddress)?; let stake_address = match parsed { Address::Shelley(x) => pallas_extras::shelley_address_to_stake_address(&x), @@ -97,7 +97,7 @@ fn parse_account_key_param(address: &str, network: Network) -> Result None, }; - stake_address.ok_or(StatusCode::BAD_REQUEST)? + stake_address.ok_or(Error::InvalidStakeAddress)? }; let stake_cred = dolos_cardano::pallas_extras::stake_address_to_cred(&address); @@ -193,7 +193,7 @@ impl<'a> IntoModel> for AccountModelBuilder<'a pub async fn by_stake( Path(stake_address): Path, State(domain): State>, -) -> Result, StatusCode> +) -> Result, Error> where Option: From, Option: From, @@ -1223,7 +1223,7 @@ mod tests { let assert_bad_request = |address: String| { let result = parse_account_key_param(&address, network).err(); - assert_eq!(result, Some(StatusCode::BAD_REQUEST)); + assert!(matches!(result, Some(Error::InvalidStakeAddress))); }; // `stake_vk` requires a 32-byte key.