Repository: CosmWasm/cosmwasm
Pull request #2101 (2024-04-10 08:29:37)

fix: avoid div with zero in assert_approx_eq
Currently, when both values are zero `assert_approx_eq` fails due to division by zero rather than succeeding because the values are the same. This fixes that.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e61db91ae..fb23dc353 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,12 @@ and this project adheres to
 - cosmwasm-std: Implement `&T + T` and `&T op &T` for `Uint64`, `Uint128`,
   `Uint256` and `Uint512`; improve panic message for `Uint64::add` and
   `Uint512::add` ([#2092])
+- cosmwasm-std: Add `{CosmosMsg,SubMsg,Response}::change_custom` to change the
+  custom message type ([#2099])
+- cosmwasm-std: Add `Uint{64,128,256,512}::strict_add` and `::strict_sub` which
+  are like the `Add`/`Sub` implementations but `const`. ([#2098], [#2107])
+- cosmwasm-std: Let `Timestamp::plus_nanos`/`::minus_nanos` use
+  `Uint64::strict_add`/`::strict_sub` and document overflows. ([#2098], [#2107])
 
 [#1983]: https://github.com/CosmWasm/cosmwasm/pull/1983
 [#2057]: https://github.com/CosmWasm/cosmwasm/pull/2057
@@ -28,6 +34,9 @@ and this project adheres to
 [#2068]: https://github.com/CosmWasm/cosmwasm/pull/2068
 [#2075]: https://github.com/CosmWasm/cosmwasm/pull/2075
 [#2092]: https://github.com/CosmWasm/cosmwasm/pull/2092
+[#2098]: https://github.com/CosmWasm/cosmwasm/pull/2098
+[#2099]: https://github.com/CosmWasm/cosmwasm/pull/2099
+[#2107]: https://github.com/CosmWasm/cosmwasm/pull/2107
 
 ### Changed
 
@@ -39,6 +48,7 @@ and this project adheres to
 - cosmwasm-schema-derive: Improve emitted error messages ([#2063])
 - cosmwasm-schema: `#[cw_serde]` now doesn't add `#[serde(deny_unknown_fields)]`
   to the expanded code anymore ([#2080])
+- cosmwasm-std: Improve performance of `Uint{64,128,256,512}::isqrt` ([#2108])
 
 [#2044]: https://github.com/CosmWasm/cosmwasm/pull/2044
 [#2051]: https://github.com/CosmWasm/cosmwasm/pull/2051
@@ -46,6 +56,7 @@ and this project adheres to
 [#2063]: https://github.com/CosmWasm/cosmwasm/pull/2063
 [#2070]: https://github.com/CosmWasm/cosmwasm/pull/2070
 [#2080]: https://github.com/CosmWasm/cosmwasm/pull/2080
+[#2108]: https://github.com/CosmWasm/cosmwasm/pull/2108
 
 ## [2.0.1] - 2024-04-03
 
diff --git a/packages/core/src/math/isqrt.rs b/packages/core/src/math/isqrt.rs
index 837cada5a..64d730f12 100644
--- a/packages/core/src/math/isqrt.rs
+++ b/packages/core/src/math/isqrt.rs
@@ -15,17 +15,22 @@ where
     I: Unsigned
         + ops::Add<I, Output = I>
         + ops::Div<I, Output = I>
+        + ops::Shl<u32, Output = I>
         + ops::Shr<u32, Output = I>
         + cmp::PartialOrd
-        + Copy
-        + From<u8>,
+        + Copy,
 {
     /// Algorithm adapted from
     /// [Wikipedia](https://en.wikipedia.org/wiki/Integer_square_root#Example_implementation_in_C).
     fn isqrt(self) -> Self {
-        let mut x0 = self >> 1;
+        // sqrt(0) = 0, sqrt(1) = 1
+        if self <= Self::ONE {
+            return self;
+        }
+
+        let mut x0 = Self::ONE << ((self.log_2() / 2) + 1);
 
-        if x0 > 0.into() {
+        if x0 > Self::ZERO {
             let mut x1 = (x0 + self / x0) >> 1;
 
             while x1 < x0 {
@@ -40,17 +45,35 @@ where
 }
 
 /// Marker trait for types that represent unsigned integers.
-pub trait Unsigned {}
-impl Unsigned for u8 {}
-impl Unsigned for u16 {}
-impl Unsigned for u32 {}
-impl Unsigned for u64 {}
-impl Unsigned for u128 {}
-impl Unsigned for Uint64 {}
-impl Unsigned for Uint128 {}
-impl Unsigned for Uint256 {}
-impl Unsigned for Uint512 {}
-impl Unsigned for usize {}
+pub trait Unsigned {
+    const ZERO: Self;
+    const ONE: Self;
+
+    fn log_2(self) -> u32;
+}
+
+macro_rules! impl_unsigned {
+    ($type:ty, $zero:expr, $one:expr) => {
+        impl Unsigned for $type {
+            const ZERO: Self = $zero;
+            const ONE: Self = $one;
+
+            fn log_2(self) -> u32 {
+                self.ilog2()
+            }
+        }
+    };
+}
+impl_unsigned!(u8, 0, 1);
+impl_unsigned!(u16, 0, 1);
+impl_unsigned!(u32, 0, 1);
+impl_unsigned!(u64, 0, 1);
+impl_unsigned!(u128, 0, 1);
+impl_unsigned!(usize, 0, 1);
+impl_unsigned!(Uint64, Self::zero(), Self::one());
+impl_unsigned!(Uint128, Self::zero(), Self::one());
+impl_unsigned!(Uint256, Self::zero(), Self::one());
+impl_unsigned!(Uint512, Self::zero(), Self::one());
 
 #[cfg(test)]
 mod tests {
diff --git a/packages/core/src/math/uint128.rs b/packages/core/src/math/uint128.rs
index 9c04c54fc..01a35cb8c 100644
--- a/packages/core/src/math/uint128.rs
+++ b/packages/core/src/math/uint128.rs
@@ -96,6 +96,16 @@ impl Uint128 {
         self.0.pow(exp).into()
     }
 
+    /// Returns the base 2 logarithm of the number, rounded down.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if `self` is zero.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn ilog2(self) -> u32 {
+        self.0.checked_ilog2().unwrap()
+    }
+
     /// Returns `self * numerator / denominator`.
     ///
     /// Due to the nature of the integer division involved, the result is always floored.
@@ -263,6 +273,28 @@ impl Uint128 {
         Self(self.0.saturating_pow(exp))
     }
 
+    /// Strict integer addition. Computes `self + rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint128::add`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_add(self, rhs: Self) -> Self {
+        match self.0.checked_add(rhs.u128()) {
+            None => panic!("attempt to add with overflow"),
+            Some(sum) => Self(sum),
+        }
+    }
+
+    /// Strict integer subtraction. Computes `self - rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint128::sub`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_sub(self, other: Self) -> Self {
+        match self.0.checked_sub(other.u128()) {
+            None => panic!("attempt to subtract with overflow"),
+            Some(diff) => Self(diff),
+        }
+    }
+
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn abs_diff(self, other: Self) -> Self {
         Self(if self.0 < other.0 {
@@ -372,11 +404,7 @@ impl Add<Uint128> for Uint128 {
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
-        Self(
-            self.u128()
-                .checked_add(rhs.u128())
-                .expect("attempt to add with overflow"),
-        )
+        self.strict_add(rhs)
     }
 }
 forward_ref_binop!(impl Add, add for Uint128, Uint128);
@@ -385,11 +413,7 @@ impl Sub<Uint128> for Uint128 {
     type Output = Self;
 
     fn sub(self, rhs: Self) -> Self {
-        Uint128(
-            self.u128()
-                .checked_sub(rhs.u128())
-                .expect("attempt to subtract with overflow"),
-        )
+        self.strict_sub(rhs)
     }
 }
 forward_ref_binop!(impl Sub, sub for Uint128, Uint128);
@@ -1176,6 +1200,37 @@ mod tests {
         assert_eq!(a, Uint128::from(1u32));
     }
 
+    #[test]
+    fn uint128_strict_add_works() {
+        let a = Uint128::new(5);
+        let b = Uint128::new(3);
+        assert_eq!(a.strict_add(b), Uint128::new(8));
+        assert_eq!(b.strict_add(a), Uint128::new(8));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to add with overflow")]
+    fn uint128_strict_add_panics_on_overflow() {
+        let a = Uint128::MAX;
+        let b = Uint128::ONE;
+        let _ = a.strict_add(b);
+    }
+
+    #[test]
+    fn uint128_strict_sub_works() {
+        let a = Uint128::new(5);
+        let b = Uint128::new(3);
+        assert_eq!(a.strict_sub(b), Uint128::new(2));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to subtract with overflow")]
+    fn uint128_strict_sub_panics_on_overflow() {
+        let a = Uint128::ZERO;
+        let b = Uint128::ONE;
+        let _ = a.strict_sub(b);
+    }
+
     #[test]
     fn uint128_abs_diff_works() {
         let a = Uint128::from(42u32);
diff --git a/packages/core/src/math/uint256.rs b/packages/core/src/math/uint256.rs
index a81942cf1..21984ede7 100644
--- a/packages/core/src/math/uint256.rs
+++ b/packages/core/src/math/uint256.rs
@@ -168,6 +168,16 @@ impl Uint256 {
         Self(self.0.pow(exp))
     }
 
+    /// Returns the base 2 logarithm of the number, rounded down.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if `self` is zero.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn ilog2(self) -> u32 {
+        self.0.checked_ilog2().unwrap()
+    }
+
     /// Returns `self * numerator / denominator`.
     ///
     /// Due to the nature of the integer division involved, the result is always floored.
@@ -335,6 +345,28 @@ impl Uint256 {
         Self(self.0.saturating_pow(exp))
     }
 
+    /// Strict integer addition. Computes `self + rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint256::add`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_add(self, rhs: Self) -> Self {
+        match self.0.checked_add(rhs.0) {
+            None => panic!("attempt to add with overflow"),
+            Some(sum) => Self(sum),
+        }
+    }
+
+    /// Strict integer subtraction. Computes `self - rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint256::sub`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_sub(self, other: Self) -> Self {
+        match self.0.checked_sub(other.0) {
+            None => panic!("attempt to subtract with overflow"),
+            Some(diff) => Self(diff),
+        }
+    }
+
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn abs_diff(self, other: Self) -> Self {
         Self(self.0.abs_diff(other.0))
@@ -442,11 +474,7 @@ impl Add<Uint256> for Uint256 {
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
-        Self(
-            self.0
-                .checked_add(rhs.0)
-                .expect("attempt to add with overflow"),
-        )
+        self.strict_add(rhs)
     }
 }
 forward_ref_binop!(impl Add, add for Uint256, Uint256);
@@ -455,11 +483,7 @@ impl Sub<Uint256> for Uint256 {
     type Output = Self;
 
     fn sub(self, rhs: Self) -> Self {
-        Self(
-            self.0
-                .checked_sub(rhs.0)
-                .expect("attempt to subtract with overflow"),
-        )
+        self.strict_sub(rhs)
     }
 }
 forward_ref_binop!(impl Sub, sub for Uint256, Uint256);
@@ -1725,6 +1749,37 @@ mod tests {
         assert_eq!(a, Uint256::from(1u32));
     }
 
+    #[test]
+    fn uint256_strict_add_works() {
+        let a = Uint256::from(5u32);
+        let b = Uint256::from(3u32);
+        assert_eq!(a.strict_add(b), Uint256::from(8u32));
+        assert_eq!(b.strict_add(a), Uint256::from(8u32));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to add with overflow")]
+    fn uint256_strict_add_panics_on_overflow() {
+        let a = Uint256::MAX;
+        let b = Uint256::ONE;
+        let _ = a.strict_add(b);
+    }
+
+    #[test]
+    fn uint256_strict_sub_works() {
+        let a = Uint256::from(5u32);
+        let b = Uint256::from(3u32);
+        assert_eq!(a.strict_sub(b), Uint256::from(2u32));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to subtract with overflow")]
+    fn uint256_strict_sub_panics_on_overflow() {
+        let a = Uint256::ZERO;
+        let b = Uint256::ONE;
+        let _ = a.strict_sub(b);
+    }
+
     #[test]
     fn uint256_abs_diff_works() {
         let a = Uint256::from(42u32);
diff --git a/packages/core/src/math/uint512.rs b/packages/core/src/math/uint512.rs
index bd26d255a..ba12827b5 100644
--- a/packages/core/src/math/uint512.rs
+++ b/packages/core/src/math/uint512.rs
@@ -193,6 +193,16 @@ impl Uint512 {
         Self(self.0.pow(exp))
     }
 
+    /// Returns the base 2 logarithm of the number, rounded down.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if `self` is zero.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn ilog2(self) -> u32 {
+        self.0.checked_ilog2().unwrap()
+    }
+
     pub fn checked_add(self, other: Self) -> Result<Self, OverflowError> {
         self.0
             .checked_add(other.0)
@@ -298,6 +308,28 @@ impl Uint512 {
         Self(self.0.saturating_pow(exp))
     }
 
+    /// Strict integer addition. Computes `self + rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint512::add`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_add(self, rhs: Self) -> Self {
+        match self.0.checked_add(rhs.0) {
+            None => panic!("attempt to add with overflow"),
+            Some(sum) => Self(sum),
+        }
+    }
+
+    /// Strict integer subtraction. Computes `self - rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint512::sub`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_sub(self, other: Self) -> Self {
+        match self.0.checked_sub(other.0) {
+            None => panic!("attempt to subtract with overflow"),
+            Some(diff) => Self(diff),
+        }
+    }
+
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn abs_diff(self, other: Self) -> Self {
         Self(self.0.abs_diff(other.0))
@@ -421,11 +453,7 @@ impl Add<Uint512> for Uint512 {
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
-        Self(
-            self.0
-                .checked_add(rhs.0)
-                .expect("attempt to add with overflow"),
-        )
+        self.strict_add(rhs)
     }
 }
 forward_ref_binop!(impl Add, add for Uint512, Uint512);
@@ -434,7 +462,7 @@ impl Sub<Uint512> for Uint512 {
     type Output = Self;
 
     fn sub(self, rhs: Self) -> Self {
-        Uint512(self.0.checked_sub(rhs.0).unwrap())
+        self.strict_sub(rhs)
     }
 }
 forward_ref_binop!(impl Sub, sub for Uint512, Uint512);
@@ -1370,6 +1398,37 @@ mod tests {
         assert_eq!(a, Uint512::from(1u32));
     }
 
+    #[test]
+    fn uint512_strict_add_works() {
+        let a = Uint512::from(5u32);
+        let b = Uint512::from(3u32);
+        assert_eq!(a.strict_add(b), Uint512::from(8u32));
+        assert_eq!(b.strict_add(a), Uint512::from(8u32));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to add with overflow")]
+    fn uint512_strict_add_panics_on_overflow() {
+        let a = Uint512::MAX;
+        let b = Uint512::ONE;
+        let _ = a.strict_add(b);
+    }
+
+    #[test]
+    fn uint512_strict_sub_works() {
+        let a = Uint512::from(5u32);
+        let b = Uint512::from(3u32);
+        assert_eq!(a.strict_sub(b), Uint512::from(2u32));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to subtract with overflow")]
+    fn uint512_strict_sub_panics_on_overflow() {
+        let a = Uint512::ZERO;
+        let b = Uint512::ONE;
+        let _ = a.strict_sub(b);
+    }
+
     #[test]
     fn uint512_abs_diff_works() {
         let a = Uint512::from(42u32);
diff --git a/packages/core/src/math/uint64.rs b/packages/core/src/math/uint64.rs
index 804cc6d75..7da2acc37 100644
--- a/packages/core/src/math/uint64.rs
+++ b/packages/core/src/math/uint64.rs
@@ -90,6 +90,16 @@ impl Uint64 {
         self.0.pow(exp).into()
     }
 
+    /// Returns the base 2 logarithm of the number, rounded down.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if `self` is zero.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub fn ilog2(self) -> u32 {
+        self.0.checked_ilog2().unwrap()
+    }
+
     /// Returns `self * numerator / denominator`.
     ///
     /// Due to the nature of the integer division involved, the result is always floored.
@@ -257,6 +267,28 @@ impl Uint64 {
         Self(self.0.saturating_pow(exp))
     }
 
+    /// Strict integer addition. Computes `self + rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint64::add`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_add(self, rhs: Self) -> Self {
+        match self.0.checked_add(rhs.u64()) {
+            None => panic!("attempt to add with overflow"),
+            Some(sum) => Self(sum),
+        }
+    }
+
+    /// Strict integer subtraction. Computes `self - rhs`, panicking if overflow occurred.
+    ///
+    /// This is the same as [`Uint64::sub`] but const.
+    #[must_use = "this returns the result of the operation, without modifying the original"]
+    pub const fn strict_sub(self, other: Self) -> Self {
+        match self.0.checked_sub(other.u64()) {
+            None => panic!("attempt to subtract with overflow"),
+            Some(diff) => Self(diff),
+        }
+    }
+
     #[must_use = "this returns the result of the operation, without modifying the original"]
     pub const fn abs_diff(self, other: Self) -> Self {
         Self(if self.0 < other.0 {
@@ -345,11 +377,7 @@ impl Add<Uint64> for Uint64 {
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self {
-        Self(
-            self.u64()
-                .checked_add(rhs.u64())
-                .expect("attempt to add with overflow"),
-        )
+        self.strict_add(rhs)
     }
 }
 forward_ref_binop!(impl Add, add for Uint64, Uint64);
@@ -358,11 +386,7 @@ impl Sub<Uint64> for Uint64 {
     type Output = Self;
 
     fn sub(self, rhs: Self) -> Self {
-        Uint64(
-            self.u64()
-                .checked_sub(rhs.u64())
-                .expect("attempt to subtract with overflow"),
-        )
+        self.strict_sub(rhs)
     }
 }
 forward_ref_binop!(impl Sub, sub for Uint64, Uint64);
@@ -1090,6 +1114,37 @@ mod tests {
         assert_eq!(a, Uint64::from(1u32));
     }
 
+    #[test]
+    fn uint64_strict_add_works() {
+        let a = Uint64::new(5);
+        let b = Uint64::new(3);
+        assert_eq!(a.strict_add(b), Uint64::new(8));
+        assert_eq!(b.strict_add(a), Uint64::new(8));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to add with overflow")]
+    fn uint64_strict_add_panics_on_overflow() {
+        let a = Uint64::MAX;
+        let b = Uint64::ONE;
+        let _ = a.strict_add(b);
+    }
+
+    #[test]
+    fn uint64_strict_sub_works() {
+        let a = Uint64::new(5);
+        let b = Uint64::new(3);
+        assert_eq!(a.strict_sub(b), Uint64::new(2));
+    }
+
+    #[test]
+    #[should_panic(expected = "attempt to subtract with overflow")]
+    fn uint64_strict_sub_panics_on_overflow() {
+        let a = Uint64::ZERO;
+        let b = Uint64::ONE;
+        let _ = a.strict_sub(b);
+    }
+
     #[test]
     fn uint64_abs_diff_works() {
         let a = Uint64::from(42u32);
diff --git a/packages/std/src/results/cosmos_msg.rs b/packages/std/src/results/cosmos_msg.rs
index 4f76c49b9..c6a1a20f3 100644
--- a/packages/std/src/results/cosmos_msg.rs
+++ b/packages/std/src/results/cosmos_msg.rs
@@ -58,6 +58,32 @@ pub enum CosmosMsg<T = Empty> {
     Gov(GovMsg),
 }
 
+impl<T> CosmosMsg<T> {
+    /// Convert this this [`CosmosMsg<T>`] to a [`CosmosMsg<U>`] with a different custom message type.
+    /// This allows easier interactions between code written for a specific chain and
+    /// code written for multiple chains.
+    /// If this is the [`CosmosMsg::Custom`] variant, the function returns `None`.
+    pub fn change_custom<U>(self) -> Option<CosmosMsg<U>> {
+        Some(match self {
+            CosmosMsg::Bank(msg) => CosmosMsg::Bank(msg),
+            CosmosMsg::Custom(_) => return None,
+            #[cfg(feature = "staking")]
+            CosmosMsg::Staking(msg) => CosmosMsg::Staking(msg),
+            #[cfg(feature = "staking")]
+            CosmosMsg::Distribution(msg) => CosmosMsg::Distribution(msg),
+            #[cfg(feature = "stargate")]
+            CosmosMsg::Stargate { type_url, value } => CosmosMsg::Stargate { type_url, value },
+            #[cfg(feature = "cosmwasm_2_0")]
+            CosmosMsg::Any(msg) => CosmosMsg::Any(msg),
+            #[cfg(feature = "stargate")]
+            CosmosMsg::Ibc(msg) => CosmosMsg::Ibc(msg),
+            CosmosMsg::Wasm(msg) => CosmosMsg::Wasm(msg),
+            #[cfg(feature = "stargate")]
+            CosmosMsg::Gov(msg) => CosmosMsg::Gov(msg),
+        })
+    }
+}
+
 /// The message types of the bank module.
 ///
 /// See https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto
@@ -434,6 +460,7 @@ impl<T> From<GovMsg> for CosmosMsg<T> {
 mod tests {
     use super::*;
     use crate::{coin, coins};
+    use fmt::Debug;
 
     #[test]
     fn from_bank_msg_works() {
@@ -664,4 +691,31 @@ mod tests {
             );
         }
     }
+
+    #[test]
+    fn change_custom_works() {
+        #[derive(Debug, PartialEq, Eq, Clone)]
+        struct Custom {
+            _a: i32,
+        }
+        let send = BankMsg::Send {
+            to_address: "you".to_string(),
+            amount: coins(1015, "earth"),
+        };
+        // Custom to Empty
+        let msg: CosmosMsg<Custom> = send.clone().into();
+        let msg2: CosmosMsg<Empty> = msg.change_custom().unwrap();
+        assert_eq!(msg2, CosmosMsg::Bank(send.clone()));
+        let custom = CosmosMsg::Custom(Custom { _a: 5 });
+        let converted = custom.change_custom::<Empty>();
+        assert_eq!(converted, None);
+
+        // Empty to Custom
+        let msg: CosmosMsg<Empty> = send.clone().into();
+        let msg2: CosmosMsg<Custom> = msg.change_custom().unwrap();
+        assert_eq!(msg2, CosmosMsg::Bank(send));
+        let empty = CosmosMsg::Custom(Empty {});
+        let converted = empty.change_custom::<Custom>();
+        assert_eq!(converted, None);
+    }
 }
diff --git a/packages/std/src/results/response.rs b/packages/std/src/results/response.rs
index a474ce59f..11bfcc148 100644
--- a/packages/std/src/results/response.rs
+++ b/packages/std/src/results/response.rs
@@ -232,6 +232,23 @@ impl<T> Response<T> {
         self.data = Some(data.into());
         self
     }
+
+    /// Convert this [`Response<T>`] to a [`Response<U>`] with a different custom message type.
+    /// This allows easier interactions between code written for a specific chain and
+    /// code written for multiple chains.
+    /// If this contains a [`CosmosMsg::Custom`] submessage, the function returns `None`.
+    pub fn change_custom<U>(self) -> Option<Response<U>> {
+        Some(Response {
+            messages: self
+                .messages
+                .into_iter()
+                .map(|msg| msg.change_custom())
+                .collect::<Option<Vec<_>>>()?,
+            attributes: self.attributes,
+            events: self.events,
+            data: self.data,
+        })
+    }
 }
 
 #[cfg(test)]
@@ -370,4 +387,38 @@ mod tests {
         let expected = Response::<Empty>::new().add_events(events);
         assert_eq!(expected, actual);
     }
+
+    #[test]
+    fn change_custom_works() {
+        let response: Response<Empty> = Response {
+            messages: vec![SubMsg::new(BankMsg::Send {
+                to_address: "address".to_string(),
+                amount: coins(123, "earth"),
+            })],
+            attributes: vec![Attribute::new("foo", "bar")],
+            events: vec![Event::new("our_event").add_attribute("msg", "hello")],
+            data: None,
+        };
+        let converted_resp: Response<String> = response.clone().change_custom().unwrap();
+        assert_eq!(
+            converted_resp.messages,
+            vec![SubMsg::new(BankMsg::Send {
+                to_address: "address".to_string(),
+                amount: coins(123, "earth"),
+            })]
+        );
+        assert_eq!(converted_resp.attributes, response.attributes);
+        assert_eq!(converted_resp.events, response.events);
+        assert_eq!(converted_resp.data, response.data);
+
+        // response with custom message
+        let response = Response {
+            messages: vec![SubMsg::new(CosmosMsg::Custom(Empty {}))],
+            attributes: vec![Attribute::new("foo", "bar")],
+            events: vec![Event::new("our_event").add_attribute("msg", "hello")],
+            data: None,
+        };
+
+        assert_eq!(response.change_custom::<String>(), None);
+    }
 }
diff --git a/packages/std/src/results/submessages.rs b/packages/std/src/results/submessages.rs
index 18f20530d..84ab97853 100644
--- a/packages/std/src/results/submessages.rs
+++ b/packages/std/src/results/submessages.rs
@@ -148,6 +148,20 @@ impl<T> SubMsg<T> {
             gas_limit: None,
         }
     }
+
+    /// Convert this [`SubMsg<T>`] to a [`SubMsg<U>`] with a different generic type.
+    /// This allows easier interactions between code written for a specific chain and
+    /// code written for multiple chains.
+    /// If this is a [`CosmosMsg::Custom`] submessage, the function returns `None`.
+    pub fn change_custom<U>(self) -> Option<SubMsg<U>> {
+        Some(SubMsg {
+            id: self.id,
+            payload: self.payload,
+            msg: self.msg.change_custom::<U>()?,
+            gas_limit: self.gas_limit,
+            reply_on: self.reply_on,
+        })
+    }
 }
 
 /// The result object returned to `reply`. We always get the ID from the submessage
diff --git a/packages/std/src/timestamp.rs b/packages/std/src/timestamp.rs
index dbee152e1..53449cbe3 100644
--- a/packages/std/src/timestamp.rs
+++ b/packages/std/src/timestamp.rs
@@ -37,62 +37,105 @@ impl Timestamp {
         Timestamp(Uint64::new(seconds_since_epoch * 1_000_000_000))
     }
 
+    /// Adds the given amount of days to the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result exceeds the value range of [`Timestamp`].
     #[must_use = "this returns the result of the operation, without modifying the original"]
     #[inline]
     pub const fn plus_days(&self, addition: u64) -> Timestamp {
         self.plus_hours(addition * 24)
     }
 
+    /// Adds the given amount of hours to the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result exceeds the value range of [`Timestamp`].
     #[must_use = "this returns the result of the operation, without modifying the original"]
     #[inline]
     pub const fn plus_hours(&self, addition: u64) -> Timestamp {
         self.plus_minutes(addition * 60)
     }
 
+    /// Adds the given amount of minutes to the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result exceeds the value range of [`Timestamp`].
     #[must_use = "this returns the result of the operation, without modifying the original"]
     #[inline]
     pub const fn plus_minutes(&self, addition: u64) -> Timestamp {
         self.plus_seconds(addition * 60)
     }
 
+    /// Adds the given amount of seconds to the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result exceeds the value range of [`Timestamp`].
     #[must_use = "this returns the result of the operation, without modifying the original"]
+    #[inline]
     pub const fn plus_seconds(&self, addition: u64) -> Timestamp {
         self.plus_nanos(addition * 1_000_000_000)
     }
 
+    /// Adds the given amount of nanoseconds to the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result exceeds the value range of [`Timestamp`].
     #[must_use = "this returns the result of the operation, without modifying the original"]
+    // no #[inline] here as this could be shared with all the callers
     pub const fn plus_nanos(&self, addition: u64) -> Timestamp {
-        let nanos = Uint64::new(self.0.u64() + addition);
+        let nanos = self.0.strict_add(Uint64::new(addition));
         Timestamp(nanos)
     }
 
+    /// Subtracts the given amount of days from the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented.
     #[must_use = "this returns the result of the operation, without modifying the original"]
     #[inline]
     pub const fn minus_days(&self, subtrahend: u64) -> Timestamp {
         self.minus_hours(subtrahend * 24)
     }
 
+    /// Subtracts the given amount of hours from the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented.
     #[must_use = "this returns the result of the operation, without modifying the original"]
     #[inline]
     pub const fn minus_hours(&self, subtrahend: u64) -> Timestamp {
         self.minus_minutes(subtrahend * 60)
     }
 
+    /// Subtracts the given amount of minutes from the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented.
     #[must_use = "this returns the result of the operation, without modifying the original"]
     #[inline]
     pub const fn minus_minutes(&self, subtrahend: u64) -> Timestamp {
         self.minus_seconds(subtrahend * 60)
     }
 
+    /// Subtracts the given amount of seconds from the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented.
     #[must_use = "this returns the result of the operation, without modifying the original"]
+    #[inline]
     pub const fn minus_seconds(&self, subtrahend: u64) -> Timestamp {
         self.minus_nanos(subtrahend * 1_000_000_000)
     }
 
+    /// Subtracts the given amount of nanoseconds from the timestamp and
+    /// returns the result. The original value remains unchanged.
+    ///
+    /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented.
     #[must_use = "this returns the result of the operation, without modifying the original"]
+    // no #[inline] here as this could be shared with all the callers
     pub const fn minus_nanos(&self, subtrahend: u64) -> Timestamp {
-        let nanos = Uint64::new(self.0.u64() - subtrahend);
-        Timestamp(nanos)
+        Timestamp(self.0.strict_sub(Uint64::new(subtrahend)))
     }
 
     /// Returns nanoseconds since epoch
@@ -159,6 +202,13 @@ mod tests {
         assert_eq!(sum.0.u64(), 123);
     }
 
+    #[test]
+    #[should_panic(expected = "attempt to add with overflow")]
+    fn timestamp_plus_nanos_panics_on_overflow() {
+        let max = Timestamp::from_nanos(u64::MAX);
+        let _earlier = max.plus_nanos(20);
+    }
+
     #[test]
     fn timestamp_minus_seconds() {
         let earlier = Timestamp::from_seconds(123).minus_seconds(0);


Test patch:
diff --git a/packages/core/src/testing/assertions.rs b/packages/core/src/testing/assertions.rs
index 757d0d69f..45d4d874c 100644
--- a/packages/core/src/testing/assertions.rs
+++ b/packages/core/src/testing/assertions.rs
@@ -56,6 +56,13 @@ pub fn assert_approx_eq_impl<U: Into<Uint128>>(
 ) {
     let left = left.into();
     let right = right.into();
+
+    if left == right {
+        // If both values are equal, we don't need to check the relative difference.
+        // We check this first to avoid division by zero below.
+        return;
+    }
+
     let max_rel_diff = Decimal::from_str(max_rel_diff).unwrap();
 
     let largest = core::cmp::max(left, right);
@@ -128,6 +135,11 @@ mod tests {
             10_000_000_000_000_000_000_000_000_000_000_000_000_u128,
             "0.10"
         );
+        assert_approx_eq!(0_u32, 0_u32, "0.12");
+        assert_approx_eq!(1_u64, 0_u64, "1");
+        assert_approx_eq!(0_u64, 1_u64, "1");
+        assert_approx_eq!(5_u64, 0_u64, "1");
+        assert_approx_eq!(0_u64, 5_u64, "1");
     }
 
     #[test]