diff --git a/Cargo.lock b/Cargo.lock
index 82682b1dc..c4f081968 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1649,6 +1649,21 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
 [[package]]
 name = "bit_field"
 version = "0.10.1"
@@ -3215,8 +3230,8 @@ dependencies = [
  "serde_json",
  "sha2 0.10.8",
  "sqlx",
- "strum 0.24.1",
- "strum_macros 0.24.3",
+ "strum",
+ "strum_macros",
  "task-manager",
  "tempfile",
  "thiserror",
@@ -3816,8 +3831,8 @@ dependencies = [
  "prost-build",
  "serde",
  "serde_json",
- "strum 0.26.3",
- "strum_macros 0.26.4",
+ "strum",
+ "strum_macros",
  "tonic",
  "tonic-build",
 ]
@@ -5167,6 +5182,7 @@ dependencies = [
  "once_cell",
  "poc-metrics",
  "price",
+ "proptest",
  "prost",
  "rand 0.8.5",
  "regex",
@@ -6050,6 +6066,26 @@ dependencies = [
  "triggered",
 ]
 
+[[package]]
+name = "proptest"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags 2.5.0",
+ "lazy_static",
+ "num-traits",
+ "rand 0.8.5",
+ "rand_chacha 0.3.0",
+ "rand_xorshift",
+ "regex-syntax 0.8.3",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
 [[package]]
 name = "prost"
 version = "0.12.4"
@@ -6192,6 +6228,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
 [[package]]
 name = "quinn"
 version = "0.10.2"
@@ -6326,6 +6368,15 @@ dependencies = [
  "rand_core 0.5.1",
 ]
 
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core 0.6.4",
+]
+
 [[package]]
 name = "rand_xoshiro"
 version = "0.6.0"
@@ -6900,6 +6951,18 @@ version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
 
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.11"
@@ -8647,35 +8710,13 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
-[[package]]
-name = "strum"
-version = "0.24.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
-dependencies = [
- "strum_macros 0.24.3",
-]
-
 [[package]]
 name = "strum"
 version = "0.26.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
 dependencies = [
- "strum_macros 0.26.4",
-]
-
-[[package]]
-name = "strum_macros"
-version = "0.24.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
-dependencies = [
- "heck 0.4.0",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn 1.0.109",
+ "strum_macros",
 ]
 
 [[package]]
@@ -9284,6 +9325,12 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c52b4cb7830f995903b2fcff3f523d21efc1c11f6c1596dd544b7925a64ff56"
 
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.15"
@@ -9476,6 +9523,15 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
 
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "waker-fn"
 version = "1.1.0"
diff --git a/mobile_verifier/Cargo.toml b/mobile_verifier/Cargo.toml
index 29d0989ca..84e6c61b3 100644
--- a/mobile_verifier/Cargo.toml
+++ b/mobile_verifier/Cargo.toml
@@ -63,3 +63,4 @@ coverage-map = { path = "../coverage_map" }
 
 [dev-dependencies]
 backon = "0"
+proptest = "1.5.0"
diff --git a/mobile_verifier/src/service_provider/reward.rs b/mobile_verifier/src/service_provider/reward.rs
index c22a03e65..3c9e7e7cc 100644
--- a/mobile_verifier/src/service_provider/reward.rs
+++ b/mobile_verifier/src/service_provider/reward.rs
@@ -284,7 +284,7 @@ mod tests {
     use file_store::promotion_reward::Entity;
     use helium_proto::services::poc_mobile::{MobileRewardShare, PromotionReward};
 
-    use crate::service_provider::promotions::rewards::PromotionRewardShare;
+    use crate::service_provider::{self, promotions::rewards::PromotionRewardShare};
 
     use super::*;
 
@@ -617,4 +617,108 @@ mod tests {
                 .collect()
         }
     }
+
+    use proptest::prelude::*;
+
+    prop_compose! {
+        fn arb_share()(sp_id in 0..10_i32, ent_id in 0..200u8, shares in 1..=100u64) -> PromotionRewardShare  {
+            PromotionRewardShare {
+                service_provider_id: sp_id,
+                rewardable_entity: Entity::SubscriberId(vec![ent_id]),
+                shares
+            }
+        }
+    }
+
+    prop_compose! {
+        fn arb_dc_session()(
+            sp_id in 0..10_i32,
+            // below 1 trillion
+            dc_session in (0..=1_000_000_000_000_u64).prop_map(Decimal::from)
+        ) -> (i32, Decimal) {
+            (sp_id, dc_session)
+        }
+    }
+
+    prop_compose! {
+        fn arb_fund()(sp_id in 0..10_i32, bps in arb_bps()) -> (i32, u16) {
+            (sp_id, bps)
+        }
+    }
+
+    prop_compose! {
+        fn arb_bps()(bps in 0..=10_000u16) -> u16 { bps }
+    }
+
+    proptest! {
+        // #![proptest_config(ProptestConfig::with_cases(100_000))]
+
+        #[test]
+        fn single_provider_does_not_overallocate(
+            dc_session in any::<u64>().prop_map(Decimal::from),
+            fund_bps in arb_bps(),
+            shares in prop::collection::vec(arb_share(), 0..10),
+            total_allocation in any::<u64>().prop_map(Decimal::from)
+        ) {
+
+            let sp_infos = ServiceProviderRewardInfos::new(
+                ServiceProviderDCSessions::from([(0, dc_session)]),
+                ServiceProviderFunds::from([(0, fund_bps)]),
+                ServiceProviderPromotions::from(shares),
+                total_allocation,
+                dec!(0.00001),
+                epoch()
+            );
+
+            let total_perc= sp_infos.total_percent();
+            assert!(total_perc <= dec!(1));
+
+            let mut allocated = dec!(0);
+            for (amount, _) in sp_infos.iter_rewards() {
+                allocated += Decimal::from(amount);
+            }
+            assert!(allocated <= total_allocation);
+        }
+
+        #[test]
+        fn multiple_provider_does_not_overallocate(
+            dc_sessions in prop::collection::vec(arb_dc_session(), 0..10),
+            funds in prop::collection::vec(arb_fund(), 0..10),
+            promotions in prop::collection::vec(arb_share(), 0..100),
+        ) {
+            let epoch = epoch();
+            let total_allocation = service_provider::get_scheduled_tokens(&epoch);
+
+            let sp_infos = ServiceProviderRewardInfos::new(
+                ServiceProviderDCSessions::from(dc_sessions),
+                ServiceProviderFunds::from(funds),
+                ServiceProviderPromotions::from(promotions),
+                total_allocation,
+                dec!(0.00001),
+                epoch
+            );
+
+            let total_perc= sp_infos.total_percent();
+            prop_assert!(total_perc <= dec!(1));
+
+            let mut allocated = dec!(0);
+            for (amount, _) in sp_infos.iter_rewards() {
+                allocated += Decimal::from(amount);
+            }
+            prop_assert!(allocated <= total_allocation);
+        }
+
+    }
+
+    impl RewardInfo {
+        fn total_percent(&self) -> Decimal {
+            self.realized_dc_perc + self.realized_promo_perc + self.matched_promo_perc
+        }
+    }
+
+    impl ServiceProviderRewardInfos {
+        fn total_percent(&self) -> Decimal {
+            self.coll.iter().map(|x| x.total_percent()).sum()
+        }
+    }
 }