Parcourir la source

feat: try_sum for amounts

thesimplekid il y a 6 mois
Parent
commit
1f81b24f40

+ 6 - 3
bindings/cdk-js/src/nuts/nut03.rs

@@ -51,13 +51,13 @@ impl JsSwapRequest {
     /// Proofs Amount
     #[wasm_bindgen(js_name = proofsAmount)]
     pub fn proofs_amount(&self) -> JsAmount {
-        self.inner.input_amount().into()
+        self.inner.input_amount().expect("Amount overflow").into()
     }
 
     /// Output Amount
     #[wasm_bindgen(js_name = outputAmount)]
     pub fn output_amount(&self) -> JsAmount {
-        self.inner.output_amount().into()
+        self.inner.output_amount().expect("Amount overflow").into()
     }
 }
 
@@ -99,6 +99,9 @@ impl JsSwapResponse {
     /// Promises Amount
     #[wasm_bindgen(js_name = promisesAmount)]
     pub fn promises_amount(&self) -> JsAmount {
-        self.inner.promises_amount().into()
+        self.inner
+            .promises_amount()
+            .expect("Amount overflow")
+            .into()
     }
 }

+ 1 - 1
bindings/cdk-js/src/nuts/nut04.rs

@@ -102,7 +102,7 @@ impl JsMintBolt11Request {
 
     #[wasm_bindgen(js_name = totalAmount)]
     pub fn total_amount(&self) -> JsAmount {
-        self.inner.total_amount().into()
+        self.inner.total_amount().expect("Amount overflow").into()
     }
 }
 

+ 4 - 1
crates/cdk-axum/src/router_handlers.rs

@@ -223,7 +223,10 @@ pub async fn post_melt_bolt11(
         }
     };
 
-    let inputs_amount_quote_unit = payload.proofs_amount();
+    let inputs_amount_quote_unit = payload.proofs_amount().map_err(|_| {
+        tracing::error!("Proof inputs in melt quote overflowed");
+        into_response(Error::AmountOverflow)
+    })?;
 
     let (preimage, amount_spent_quote_unit) = match mint_quote {
         Some(mint_quote) => {

+ 6 - 8
crates/cdk-integration-tests/tests/overflow.rs

@@ -37,8 +37,7 @@ async fn attempt_to_swap_by_overflowing() -> Result<()> {
         sleep(Duration::from_secs(2)).await;
     }
 
-    let premint_secrets =
-        PreMintSecrets::random(keyset_id.clone(), 1.into(), &SplitTarget::default())?;
+    let premint_secrets = PreMintSecrets::random(keyset_id, 1.into(), &SplitTarget::default())?;
 
     let mint_response = wallet_client
         .post_mint(
@@ -60,12 +59,11 @@ async fn attempt_to_swap_by_overflowing() -> Result<()> {
     let amount = 2_u64.pow(63);
 
     let pre_mint_amount =
-        PreMintSecrets::random(keyset_id.clone(), amount.into(), &SplitTarget::default())?;
+        PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
     let pre_mint_amount_two =
-        PreMintSecrets::random(keyset_id.clone(), amount.into(), &SplitTarget::default())?;
+        PreMintSecrets::random(keyset_id, amount.into(), &SplitTarget::default())?;
 
-    let mut pre_mint =
-        PreMintSecrets::random(keyset_id.clone(), 1.into(), &SplitTarget::default())?;
+    let mut pre_mint = PreMintSecrets::random(keyset_id, 1.into(), &SplitTarget::default())?;
 
     pre_mint.combine(pre_mint_amount);
     pre_mint.combine(pre_mint_amount_two);
@@ -91,11 +89,11 @@ async fn attempt_to_swap_by_overflowing() -> Result<()> {
 
     println!(
         "Pre swap amount: {:?}",
-        pre_swap_proofs.iter().map(|p| p.amount).sum::<Amount>()
+        Amount::try_sum(pre_swap_proofs.iter().map(|p| p.amount)).expect("Amount overflowed")
     );
     println!(
         "Post swap amount: {:?}",
-        post_swap_proofs.iter().map(|p| p.amount).sum::<Amount>()
+        Amount::try_sum(post_swap_proofs.iter().map(|p| p.amount)).expect("Amount Overflowed")
     );
 
     println!(

+ 3 - 12
crates/cdk/src/amount.rs

@@ -54,7 +54,7 @@ impl Amount {
                             parts.extend(amount_left.split());
                         }
 
-                        parts_total = parts.clone().iter().copied().sum::<Amount>();
+                        parts_total = Amount::try_sum(parts.clone().iter().copied())?;
 
                         if parts_total.eq(self) {
                             break;
@@ -65,7 +65,7 @@ impl Amount {
                 parts
             }
             SplitTarget::Values(values) => {
-                let values_total: Amount = values.clone().into_iter().sum();
+                let values_total: Amount = Amount::try_sum(values.clone().into_iter())?;
 
                 match self.cmp(&values_total) {
                     Ordering::Equal => values.clone(),
@@ -190,15 +190,6 @@ impl std::ops::Div for Amount {
     }
 }
 
-impl core::iter::Sum for Amount {
-    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
-        iter.fold(Amount::ZERO, |acc, x| {
-            acc.checked_add(x)
-                .unwrap_or_else(|| panic!("Addition overflow"))
-        })
-    }
-}
-
 /// Kinds of targeting that are supported
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
 pub enum SplitTarget {
@@ -314,7 +305,7 @@ mod tests {
 
         let amounts = vec![amount_one, amount_two];
 
-        let _total: Amount = amounts.into_iter().sum();
+        let _total: Amount = Amount::try_sum(amounts).unwrap();
     }
 
     #[test]

+ 9 - 0
crates/cdk/src/mint/error.rs

@@ -20,6 +20,9 @@ pub enum Error {
     /// Amount is not what is expected
     #[error("Amount")]
     Amount,
+    /// Amount overflow
+    #[error("Amount Overflow")]
+    AmountOverflow,
     /// Not engough inputs provided
     #[error("Inputs: `{0}`, Outputs: `{0}`, Fee: `{0}`")]
     InsufficientInputs(u64, u64, u64),
@@ -89,6 +92,12 @@ pub enum Error {
     /// NUT00 Error
     #[error(transparent)]
     NUT00(#[from] crate::nuts::nut00::Error),
+    /// NUT04 Error
+    #[error(transparent)]
+    NUT04(#[from] crate::nuts::nut04::Error),
+    /// NUT05 Error
+    #[error(transparent)]
+    NUT05(#[from] crate::nuts::nut05::Error),
     /// NUT11 Error
     #[error(transparent)]
     NUT11(#[from] crate::nuts::nut11::Error),

+ 14 - 15
crates/cdk/src/mint/mod.rs

@@ -705,13 +705,13 @@ impl Mint {
             return Err(Error::BlindedMessageAlreadySigned);
         }
 
-        let proofs_total = swap_request.input_amount();
+        let proofs_total = swap_request.input_amount()?;
 
-        let output_total = swap_request.output_amount();
+        let output_total = swap_request.output_amount()?;
 
         let fee = self.get_proofs_fee(&swap_request.inputs).await?;
 
-        if proofs_total < output_total + fee {
+        if proofs_total < output_total.checked_add(fee).ok_or(Error::AmountOverflow)? {
             tracing::info!(
                 "Swap request without enough inputs: {}, outputs {}, fee {}",
                 proofs_total,
@@ -989,7 +989,7 @@ impl Mint {
             .await?
             .ok_or(Error::UnknownQuote)?;
 
-        let proofs_total = melt_request.proofs_amount();
+        let proofs_total = melt_request.proofs_amount()?;
 
         let fee = self.get_proofs_fee(&melt_request.inputs).await?;
 
@@ -1121,7 +1121,7 @@ impl Mint {
         let mut change = None;
 
         // Check if there is change to return
-        if melt_request.proofs_amount() > total_spent {
+        if melt_request.proofs_amount()? > total_spent {
             // Check if wallet provided change outputs
             if let Some(outputs) = melt_request.outputs.clone() {
                 let blinded_messages: Vec<PublicKey> =
@@ -1141,7 +1141,7 @@ impl Mint {
                     return Err(Error::BlindedMessageAlreadySigned);
                 }
 
-                let change_target = melt_request.proofs_amount() - total_spent;
+                let change_target = melt_request.proofs_amount()? - total_spent;
                 let mut amounts = change_target.split();
                 let mut change_sigs = Vec::with_capacity(amounts.len());
 
@@ -1271,7 +1271,7 @@ impl Mint {
                 .get_blind_signatures_for_keyset(&keyset.id)
                 .await?;
 
-            let total = blinded.iter().map(|b| b.amount).sum();
+            let total = Amount::try_sum(blinded.iter().map(|b| b.amount))?;
 
             total_issued.insert(keyset.id, total);
         }
@@ -1289,14 +1289,13 @@ impl Mint {
         for keyset in keysets {
             let (proofs, state) = self.localstore.get_proofs_by_keyset_id(&keyset.id).await?;
 
-            let total_spent = proofs
-                .iter()
-                .zip(state)
-                .filter_map(|(p, s)| match s == Some(State::Spent) {
-                    true => Some(p.amount),
-                    false => None,
-                })
-                .sum();
+            let total_spent =
+                Amount::try_sum(proofs.iter().zip(state).filter_map(|(p, s)| {
+                    match s == Some(State::Spent) {
+                        true => Some(p.amount),
+                        false => None,
+                    }
+                }))?;
 
             total_redeemed.insert(keyset.id, total_spent);
         }

+ 4 - 5
crates/cdk/src/nuts/nut00/mod.rs

@@ -609,11 +609,10 @@ impl PreMintSecrets {
     }
 
     /// Totoal amount of secrets
-    pub fn total_amount(&self) -> Amount {
-        self.secrets
-            .iter()
-            .map(|PreMint { amount, .. }| *amount)
-            .sum()
+    pub fn total_amount(&self) -> Result<Amount, Error> {
+        Ok(Amount::try_sum(
+            self.secrets.iter().map(|PreMint { amount, .. }| *amount),
+        )?)
     }
 
     /// [`BlindedMessage`]s from [`PreMintSecrets`]

+ 16 - 12
crates/cdk/src/nuts/nut00/token.rs

@@ -74,7 +74,7 @@ impl Token {
     }
 
     /// Total value of [`Token`]
-    pub fn value(&self) -> Amount {
+    pub fn value(&self) -> Result<Amount, Error> {
         match self {
             Self::TokenV3(token) => token.value(),
             Self::TokenV4(token) => token.value(),
@@ -207,11 +207,13 @@ impl TokenV3 {
     }
 
     #[inline]
-    fn value(&self) -> Amount {
-        self.token
-            .iter()
-            .map(|t| t.proofs.iter().map(|p| p.amount).sum())
-            .sum()
+    fn value(&self) -> Result<Amount, Error> {
+        Ok(Amount::try_sum(
+            self.token
+                .iter()
+                .map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
+                .collect::<Result<Vec<Amount>, _>>()?,
+        )?)
     }
 
     #[inline]
@@ -305,11 +307,13 @@ impl TokenV4 {
     }
 
     #[inline]
-    fn value(&self) -> Amount {
-        self.token
-            .iter()
-            .map(|t| t.proofs.iter().map(|p| p.amount).sum())
-            .sum()
+    fn value(&self) -> Result<Amount, Error> {
+        Ok(Amount::try_sum(
+            self.token
+                .iter()
+                .map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
+                .collect::<Result<Vec<Amount>, _>>()?,
+        )?)
     }
 
     #[inline]
@@ -464,7 +468,7 @@ mod tests {
         let token_str_multi_keysets = "cashuBo2F0gqJhaUgA_9SLj17PgGFwgaNhYQFhc3hAYWNjMTI0MzVlN2I4NDg0YzNjZjE4NTAxNDkyMThhZjkwZjcxNmE1MmJmNGE1ZWQzNDdlNDhlY2MxM2Y3NzM4OGFjWCECRFODGd5IXVW-07KaZCvuWHk3WrnnpiDhHki6SCQh88-iYWlIAK0mjE0fWCZhcIKjYWECYXN4QDEzMjNkM2Q0NzA3YTU4YWQyZTIzYWRhNGU5ZjFmNDlmNWE1YjRhYzdiNzA4ZWIwZDYxZjczOGY0ODMwN2U4ZWVhY1ghAjRWqhENhLSsdHrr2Cw7AFrKUL9Ffr1XN6RBT6w659lNo2FhAWFzeEA1NmJjYmNiYjdjYzY0MDZiM2ZhNWQ1N2QyMTc0ZjRlZmY4YjQ0MDJiMTc2OTI2ZDNhNTdkM2MzZGNiYjU5ZDU3YWNYIQJzEpxXGeWZN5qXSmJjY8MzxWyvwObQGr5G1YCCgHicY2FtdWh0dHA6Ly9sb2NhbGhvc3Q6MzMzOGF1Y3NhdA==";
 
         let token = Token::from_str(token_str_multi_keysets).unwrap();
-        let amount = token.value();
+        let amount = token.value()?;
 
         assert_eq!(amount, Amount::from(4));
 

+ 11 - 10
crates/cdk/src/nuts/nut03.rs

@@ -5,7 +5,7 @@
 use serde::{Deserialize, Serialize};
 
 use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
-use crate::Amount;
+use crate::{error::Error, Amount};
 
 /// Preswap information
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
@@ -36,13 +36,13 @@ impl SwapRequest {
     }
 
     /// Total value of proofs in [`SwapRequest`]
-    pub fn input_amount(&self) -> Amount {
-        self.inputs.iter().map(|proof| proof.amount).sum()
+    pub fn input_amount(&self) -> Result<Amount, Error> {
+        Amount::try_sum(self.inputs.iter().map(|proof| proof.amount))
     }
 
     /// Total value of outputs in [`SwapRequest`]
-    pub fn output_amount(&self) -> Amount {
-        self.outputs.iter().map(|proof| proof.amount).sum()
+    pub fn output_amount(&self) -> Result<Amount, Error> {
+        Amount::try_sum(self.outputs.iter().map(|proof| proof.amount))
     }
 }
 
@@ -62,10 +62,11 @@ impl SwapResponse {
     }
 
     /// Total [`Amount`] of promises
-    pub fn promises_amount(&self) -> Amount {
-        self.signatures
-            .iter()
-            .map(|BlindSignature { amount, .. }| *amount)
-            .sum()
+    pub fn promises_amount(&self) -> Result<Amount, Error> {
+        Amount::try_sum(
+            self.signatures
+                .iter()
+                .map(|BlindSignature { amount, .. }| *amount),
+        )
     }
 }

+ 10 - 5
crates/cdk/src/nuts/nut04.rs

@@ -19,6 +19,9 @@ pub enum Error {
     /// Unknown Quote State
     #[error("Unknown Quote State")]
     UnknownState,
+    /// Amount overflow
+    #[error("Amount overflow")]
+    AmountOverflow,
 }
 
 /// Mint quote request [NUT-04]
@@ -179,11 +182,13 @@ pub struct MintBolt11Request {
 
 impl MintBolt11Request {
     /// Total [`Amount`] of outputs
-    pub fn total_amount(&self) -> Amount {
-        self.outputs
-            .iter()
-            .map(|BlindedMessage { amount, .. }| *amount)
-            .sum()
+    pub fn total_amount(&self) -> Result<Amount, Error> {
+        Amount::try_sum(
+            self.outputs
+                .iter()
+                .map(|BlindedMessage { amount, .. }| *amount),
+        )
+        .map_err(|_| Error::AmountOverflow)
     }
 }
 

+ 6 - 2
crates/cdk/src/nuts/nut05.rs

@@ -21,6 +21,9 @@ pub enum Error {
     /// Unknown Quote State
     #[error("Unknown quote state")]
     UnknownState,
+    /// Amount overflow
+    #[error("Amount Overflow")]
+    AmountOverflow,
 }
 
 /// Melt quote request [NUT-05]
@@ -210,8 +213,9 @@ pub struct MeltBolt11Request {
 
 impl MeltBolt11Request {
     /// Total [`Amount`] of [`Proofs`]
-    pub fn proofs_amount(&self) -> Amount {
-        self.inputs.iter().map(|proof| proof.amount).sum()
+    pub fn proofs_amount(&self) -> Result<Amount, Error> {
+        Amount::try_sum(self.inputs.iter().map(|proof| proof.amount))
+            .map_err(|_| Error::AmountOverflow)
     }
 }
 

+ 2 - 2
crates/cdk/src/nuts/nut08.rs

@@ -10,7 +10,7 @@ impl MeltBolt11Request {
     pub fn output_amount(&self) -> Option<Amount> {
         self.outputs
             .as_ref()
-            .map(|o| o.iter().map(|proof| proof.amount).sum())
+            .and_then(|o| Amount::try_sum(o.iter().map(|proof| proof.amount)).ok())
     }
 }
 
@@ -19,6 +19,6 @@ impl MeltQuoteBolt11Response {
     pub fn change_amount(&self) -> Option<Amount> {
         self.change
             .as_ref()
-            .map(|c| c.iter().map(|b| b.amount).sum())
+            .and_then(|o| Amount::try_sum(o.iter().map(|proof| proof.amount)).ok())
     }
 }

+ 3 - 0
crates/cdk/src/wallet/error.rs

@@ -39,6 +39,9 @@ pub enum Error {
     /// Unknown Key
     #[error("Unknown key")]
     UnknownKey,
+    /// Amount overflow
+    #[error("Amount Overflow")]
+    AmountOverflow,
     /// Spending Locktime not provided
     #[error("Spending condition locktime not provided")]
     LocktimeNotProvided,

+ 19 - 19
crates/cdk/src/wallet/mod.rs

@@ -145,7 +145,7 @@ impl Wallet {
                 None,
             )
             .await?;
-        let balance = proofs.iter().map(|p| p.proof.amount).sum::<Amount>();
+        let balance = Amount::try_sum(proofs.iter().map(|p| p.proof.amount))?;
 
         Ok(balance)
     }
@@ -457,7 +457,7 @@ impl Wallet {
             .into_iter()
             .partition(|p| pending_states.contains(&p.y));
 
-        let amount = pending_proofs.iter().map(|p| p.proof.amount).sum();
+        let amount = Amount::try_sum(pending_proofs.iter().map(|p| p.proof.amount))?;
 
         self.localstore
             .update_proofs(
@@ -678,7 +678,7 @@ impl Wallet {
             &keys,
         )?;
 
-        let minted_amount = proofs.iter().map(|p| p.amount).sum();
+        let minted_amount = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
 
         // Remove filled quote from store
         self.localstore.remove_mint_quote(&quote_info.id).await?;
@@ -754,7 +754,7 @@ impl Wallet {
         let mut values = Vec::new();
 
         for amount in amounts_needed_refill {
-            let values_sum: Amount = values.clone().into_iter().sum();
+            let values_sum = Amount::try_sum(values.clone().into_iter())?;
             if values_sum + amount <= change_amount {
                 values.push(amount);
             }
@@ -776,7 +776,7 @@ impl Wallet {
         let active_keyset_id = self.get_active_mint_keyset().await?.id;
 
         // Desired amount is either amount passed or value of all proof
-        let proofs_total: Amount = proofs.iter().map(|p| p.amount).sum();
+        let proofs_total = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
 
         let ys: Vec<PublicKey> = proofs.iter().map(|p| p.y()).collect::<Result<_, _>>()?;
         self.localstore.set_pending_proofs(ys).await?;
@@ -953,7 +953,7 @@ impl Wallet {
 
                         for proof in all_proofs {
                             let proofs_to_send_amount =
-                                proofs_to_send.iter().map(|p| p.amount).sum::<Amount>();
+                                Amount::try_sum(proofs_to_send.iter().map(|p| p.amount))?;
                             if proof.amount + proofs_to_send_amount <= amount + pre_swap.fee {
                                 proofs_to_send.push(proof);
                             } else {
@@ -965,7 +965,7 @@ impl Wallet {
                     }
                 };
 
-                let send_amount: Amount = proofs_to_send.iter().map(|p| p.amount).sum();
+                let send_amount = Amount::try_sum(proofs_to_send.iter().map(|p| p.amount))?;
 
                 if send_amount.ne(&(amount + pre_swap.fee)) {
                     tracing::warn!(
@@ -1158,7 +1158,7 @@ impl Wallet {
             // Handle exact matches offline
             (SendKind::OfflineExact, Ok(selected_proofs), _) => {
                 let selected_proofs_amount =
-                    selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
 
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -1175,7 +1175,7 @@ impl Wallet {
             // Handle exact matches
             (SendKind::OnlineExact, Ok(selected_proofs), _) => {
                 let selected_proofs_amount =
-                    selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
 
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -1196,7 +1196,7 @@ impl Wallet {
             // Handle offline tolerance
             (SendKind::OfflineTolerance(tolerance), Ok(selected_proofs), _) => {
                 let selected_proofs_amount =
-                    selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
 
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -1222,7 +1222,7 @@ impl Wallet {
             // Handle online tolerance with successful selection
             (SendKind::OnlineTolerance(tolerance), Ok(selected_proofs), _) => {
                 let selected_proofs_amount =
-                    selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
                     false => amount,
@@ -1435,7 +1435,7 @@ impl Wallet {
             Some(change_proofs) => {
                 tracing::debug!(
                     "Change amount returned from melt: {}",
-                    change_proofs.iter().map(|p| p.amount).sum::<Amount>()
+                    Amount::try_sum(change_proofs.iter().map(|p| p.amount))?
                 );
 
                 // Update counter for keyset
@@ -1532,7 +1532,7 @@ impl Wallet {
     ) -> Result<Proofs, Error> {
         // TODO: Check all proofs are same unit
 
-        if proofs.iter().map(|p| p.amount).sum::<Amount>() < amount {
+        if Amount::try_sum(proofs.iter().map(|p| p.amount))? < amount {
             return Err(Error::InsufficientFunds);
         }
 
@@ -1571,8 +1571,8 @@ impl Wallet {
                 break;
             }
 
-            remaining_amount =
-                amount + fees - selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+            remaining_amount = amount.checked_add(fees).ok_or(Error::AmountOverflow)?
+                - Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
             (proofs_larger, proofs_smaller) = proofs_smaller
                 .into_iter()
                 .skip(1)
@@ -1608,7 +1608,7 @@ impl Wallet {
 
         for inactive_proof in inactive_proofs {
             selected_proofs.push(inactive_proof);
-            let selected_total = selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+            let selected_total = Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
             let fees = self.get_proofs_fee(&selected_proofs).await?;
 
             if selected_total >= amount + fees {
@@ -1620,7 +1620,7 @@ impl Wallet {
 
         for active_proof in active_proofs {
             selected_proofs.push(active_proof);
-            let selected_total = selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
+            let selected_total = Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
             let fees = self.get_proofs_fee(&selected_proofs).await?;
 
             if selected_total >= amount + fees {
@@ -1767,7 +1767,7 @@ impl Wallet {
 
         let mut total_amount = Amount::ZERO;
         for (mint, proofs) in received_proofs {
-            total_amount += proofs.iter().map(|p| p.amount).sum();
+            total_amount += Amount::try_sum(proofs.iter().map(|p| p.amount))?;
             let proofs = proofs
                 .into_iter()
                 .map(|proof| ProofInfo::new(proof, mint.clone(), State::Unspent, self.unit))
@@ -1927,7 +1927,7 @@ impl Wallet {
                     .cloned()
                     .collect();
 
-                restored_value += unspent_proofs.iter().map(|p| p.amount).sum();
+                restored_value += Amount::try_sum(unspent_proofs.iter().map(|p| p.amount))?;
 
                 let unspent_proofs = unspent_proofs
                     .into_iter()