diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs
index de989e8943de5caa3631c07b5d9e331d90b9fde1..e6250bb9681a04c5db219864493e850422c1affd 100644
--- a/substrate/frame/staking/src/lib.rs
+++ b/substrate/frame/staking/src/lib.rs
@@ -530,12 +530,12 @@ impl<T: Config> StakingLedger<T> {
 		let mut unlocking_balance = BalanceOf::<T>::zero();
 
 		while let Some(last) = self.unlocking.last_mut() {
-			if unlocking_balance + last.value <= value {
+			if unlocking_balance.defensive_saturating_add(last.value) <= value {
 				unlocking_balance += last.value;
 				self.active += last.value;
 				self.unlocking.pop();
 			} else {
-				let diff = value - unlocking_balance;
+				let diff = value.defensive_saturating_sub(unlocking_balance);
 
 				unlocking_balance += diff;
 				self.active += diff;
@@ -589,7 +589,7 @@ impl<T: Config> StakingLedger<T> {
 
 		// for a `slash_era = x`, any chunk that is scheduled to be unlocked at era `x + 28`
 		// (assuming 28 is the bonding duration) onwards should be slashed.
-		let slashable_chunks_start = slash_era + T::BondingDuration::get();
+		let slashable_chunks_start = slash_era.saturating_add(T::BondingDuration::get());
 
 		// `Some(ratio)` if this is proportional, with `ratio`, `None` otherwise. In both cases, we
 		// slash first the active chunk, and then `slash_chunks_priority`.
@@ -1185,8 +1185,9 @@ impl<T: Config> EraInfo<T> {
 
 		let nominator_count = exposure.others.len();
 		// expected page count is the number of nominators divided by the page size, rounded up.
-		let expected_page_count =
-			nominator_count.defensive_saturating_add(page_size as usize - 1) / page_size as usize;
+		let expected_page_count = nominator_count
+			.defensive_saturating_add((page_size as usize).defensive_saturating_sub(1))
+			.saturating_div(page_size as usize);
 
 		let (exposure_metadata, exposure_pages) = exposure.into_pages(page_size);
 		defensive_assert!(exposure_pages.len() == expected_page_count, "unexpected page count");
diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs
index e18d9378b5e97816db1e61c58b91500e2df3412d..d294686751cd0626821f0fda0135438e86677c97 100644
--- a/substrate/frame/staking/src/pallet/impls.rs
+++ b/substrate/frame/staking/src/pallet/impls.rs
@@ -27,8 +27,8 @@ use frame_support::{
 	dispatch::WithPostDispatchInfo,
 	pallet_prelude::*,
 	traits::{
-		Currency, Defensive, EstimateNextNewSession, Get, Imbalance, Len, OnUnbalanced, TryCollect,
-		UnixTime,
+		Currency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len,
+		OnUnbalanced, TryCollect, UnixTime,
 	},
 	weights::Weight,
 };
@@ -148,7 +148,7 @@ impl<T: Config> Pallet<T> {
 		// `consolidate_unlocked` strictly subtracts balance.
 		if new_total < old_total {
 			// Already checked that this won't overflow by entry condition.
-			let value = old_total - new_total;
+			let value = old_total.defensive_saturating_sub(new_total);
 			Self::deposit_event(Event::<T>::Withdrawn { stash, amount: value });
 		}
 
@@ -262,7 +262,8 @@ impl<T: Config> Pallet<T> {
 		// total commission validator takes across all nominator pages
 		let validator_total_commission_payout = validator_commission * validator_total_payout;
 
-		let validator_leftover_payout = validator_total_payout - validator_total_commission_payout;
+		let validator_leftover_payout =
+			validator_total_payout.defensive_saturating_sub(validator_total_commission_payout);
 		// Now let's calculate how this is split to the validator.
 		let validator_exposure_part = Perbill::from_rational(exposure.own(), exposure.total());
 		let validator_staking_payout = validator_exposure_part * validator_leftover_payout;
@@ -475,7 +476,7 @@ impl<T: Config> Pallet<T> {
 			bonded.push((active_era, start_session));
 
 			if active_era > bonding_duration {
-				let first_kept = active_era - bonding_duration;
+				let first_kept = active_era.defensive_saturating_sub(bonding_duration);
 
 				// Prune out everything that's from before the first-kept index.
 				let n_to_prune =
@@ -501,7 +502,8 @@ impl<T: Config> Pallet<T> {
 		if let Some(active_era_start) = active_era.start {
 			let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
 
-			let era_duration = (now_as_millis_u64 - active_era_start).saturated_into::<u64>();
+			let era_duration = (now_as_millis_u64.defensive_saturating_sub(active_era_start))
+				.saturated_into::<u64>();
 			let staked = Self::eras_total_stake(&active_era.index);
 			let issuance = T::Currency::total_issuance();
 			let (validator_payout, remainder) =
diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs
index a68e9c90da9e660e1d38f67c2ee44c509b324d75..1f79ef63a475907b43b26e1c4389890e5b14f9b3 100644
--- a/substrate/frame/staking/src/pallet/mod.rs
+++ b/substrate/frame/staking/src/pallet/mod.rs
@@ -1068,7 +1068,9 @@ pub mod pallet {
 				ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
 
 				// Note: in case there is no current era it is fine to bond one era more.
-				let era = Self::current_era().unwrap_or(0) + T::BondingDuration::get();
+				let era = Self::current_era()
+					.unwrap_or(0)
+					.defensive_saturating_add(T::BondingDuration::get());
 				if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
 					// To keep the chunk count down, we only keep one chunk per era. Since
 					// `unlocking` is a FiFo queue, if a chunk exists for `era` we know that it will
diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs
index 0d84d503733e6451feb69f287d69f8655fe0b20e..709fd1441ec3af313220486aa8000c959e30fd07 100644
--- a/substrate/frame/staking/src/slashing.rs
+++ b/substrate/frame/staking/src/slashing.rs
@@ -57,7 +57,7 @@ use crate::{
 use codec::{Decode, Encode, MaxEncodedLen};
 use frame_support::{
 	ensure,
-	traits::{Currency, Defensive, Get, Imbalance, OnUnbalanced},
+	traits::{Currency, Defensive, DefensiveSaturating, Get, Imbalance, OnUnbalanced},
 };
 use scale_info::TypeInfo;
 use sp_runtime::{
@@ -85,7 +85,7 @@ pub(crate) struct SlashingSpan {
 
 impl SlashingSpan {
 	fn contains_era(&self, era: EraIndex) -> bool {
-		self.start <= era && self.length.map_or(true, |l| self.start + l > era)
+		self.start <= era && self.length.map_or(true, |l| self.start.saturating_add(l) > era)
 	}
 }
 
@@ -123,15 +123,15 @@ impl SlashingSpans {
 	// returns `true` if a new span was started, `false` otherwise. `false` indicates
 	// that internal state is unchanged.
 	pub(crate) fn end_span(&mut self, now: EraIndex) -> bool {
-		let next_start = now + 1;
+		let next_start = now.defensive_saturating_add(1);
 		if next_start <= self.last_start {
 			return false
 		}
 
-		let last_length = next_start - self.last_start;
+		let last_length = next_start.defensive_saturating_sub(self.last_start);
 		self.prior.insert(0, last_length);
 		self.last_start = next_start;
-		self.span_index += 1;
+		self.span_index.defensive_saturating_accrue(1);
 		true
 	}
 
@@ -141,9 +141,9 @@ impl SlashingSpans {
 		let mut index = self.span_index;
 		let last = SlashingSpan { index, start: last_start, length: None };
 		let prior = self.prior.iter().cloned().map(move |length| {
-			let start = last_start - length;
+			let start = last_start.defensive_saturating_sub(length);
 			last_start = start;
-			index -= 1;
+			index.defensive_saturating_reduce(1);
 
 			SlashingSpan { index, start, length: Some(length) }
 		});
@@ -164,13 +164,18 @@ impl SlashingSpans {
 		let old_idx = self
 			.iter()
 			.skip(1) // skip ongoing span.
-			.position(|span| span.length.map_or(false, |len| span.start + len <= window_start));
+			.position(|span| {
+				span.length
+					.map_or(false, |len| span.start.defensive_saturating_add(len) <= window_start)
+			});
 
-		let earliest_span_index = self.span_index - self.prior.len() as SpanIndex;
+		let earliest_span_index =
+			self.span_index.defensive_saturating_sub(self.prior.len() as SpanIndex);
 		let pruned = match old_idx {
 			Some(o) => {
 				self.prior.truncate(o);
-				let new_earliest = self.span_index - self.prior.len() as SpanIndex;
+				let new_earliest =
+					self.span_index.defensive_saturating_sub(self.prior.len() as SpanIndex);
 				Some((earliest_span_index, new_earliest))
 			},
 			None => None,
@@ -500,7 +505,7 @@ impl<'a, T: 'a + Config> InspectingSpans<'a, T> {
 
 		let reward = if span_record.slashed < slash {
 			// new maximum span slash. apply the difference.
-			let difference = slash - span_record.slashed;
+			let difference = slash.defensive_saturating_sub(span_record.slashed);
 			span_record.slashed = slash;
 
 			// compute reward.