diff --git a/substrate/demo/runtime/src/runtime/council.rs b/substrate/demo/runtime/src/runtime/council.rs
index f9c30b3235196749414fb4447271af42be38c1a7..620405bdfdecb76add1b51a32b87bceb7c88a741 100644
--- a/substrate/demo/runtime/src/runtime/council.rs
+++ b/substrate/demo/runtime/src/runtime/council.rs
@@ -265,13 +265,7 @@ pub mod public {
 		assert_eq!(index, vote_index());
 		if !storage::exists(&signed.to_keyed_vec(LAST_ACTIVE_OF)) {
 			// not yet a voter - deduct bond.
-			let b = staking::balance(signed);
-			assert!(b >= voting_bond());
-			// TODO: this is no good as it precludes active stakers. check that when we allow
-			// deductions of actively staked balances that things in the staking module don't
-			// break.
-//			assert!(staking::unlock_block(signed) == staking::LockStatus::Liquid);
-			staking::internal::set_balance(signed, b - voting_bond());
+			staking::internal::reserve_balance(signed, voting_bond());
 			storage::put(VOTERS, &{
 				let mut v: Vec<AccountId> = storage::get_or_default(VOTERS);
 				v.push(signed.clone());
@@ -288,16 +282,16 @@ pub mod public {
 	///
 	/// May be called by anyone. Returns the voter deposit to `signed`.
 	pub fn reap_inactive_voter(signed: &AccountId, signed_index: u32, who: &AccountId, who_index: u32, assumed_vote_index: VoteIndex) {
-		assert!(!presentation_active());
-		assert!(voter_last_active(signed).is_some());
+		assert!(!presentation_active(), "cannot reap during presentation period");
+		assert!(voter_last_active(signed).is_some(), "reaper must be a voter");
 		let last_active = voter_last_active(who).expect("target for inactivity cleanup must be active");
-		assert!(assumed_vote_index == vote_index());
-		assert!(last_active < assumed_vote_index - inactivity_grace_period());
+		assert!(assumed_vote_index == vote_index(), "vote index not current");
+		assert!(last_active < assumed_vote_index - inactivity_grace_period(), "cannot reap during grace perid");
 		let voters = voters();
 		let signed_index = signed_index as usize;
 		let who_index = who_index as usize;
-		assert!(signed_index < voters.len() && voters[signed_index] == *signed);
-		assert!(who_index < voters.len() && voters[who_index] == *who);
+		assert!(signed_index < voters.len() && voters[signed_index] == *signed, "bad reporter index");
+		assert!(who_index < voters.len() && voters[who_index] == *who, "bad target index");
 
 		// will definitely kill one of signed or who now.
 
@@ -315,41 +309,40 @@ pub mod public {
 			voters
 		);
 		if valid {
-			staking::internal::set_balance(signed, staking::balance(signed) + voting_bond());
+			staking::internal::transfer_reserved_balance(who, signed, voting_bond());
+		} else {
+			staking::internal::slash_reserved(signed, voting_bond());
 		}
 	}
 
 	/// Remove a voter. All votes are cancelled and the voter deposit is returned.
 	pub fn retract_voter(signed: &AccountId, index: u32) {
-		assert!(!presentation_active());
-		assert!(storage::exists(&signed.to_keyed_vec(LAST_ACTIVE_OF)));
+		assert!(!presentation_active(), "cannot retract when presenting");
+		assert!(storage::exists(&signed.to_keyed_vec(LAST_ACTIVE_OF)), "cannot retract non-voter");
 		let voters = voters();
 		let index = index as usize;
-		assert!(index < voters.len() && voters[index] == *signed);
+		assert!(index < voters.len(), "retraction index invalid");
+		assert!(voters[index] == *signed, "retraction index mismatch");
 		remove_voter(signed, index, voters);
-		staking::internal::set_balance(signed, staking::balance(signed) + voting_bond());
+		staking::internal::unreserve_balance(signed, voting_bond());
 	}
 
 	/// Submit oneself for candidacy.
 	///
 	/// Account must have enough transferrable funds in it to pay the bond.
 	pub fn submit_candidacy(signed: &AccountId, slot: u32) {
-		assert!(!is_a_candidate(signed));
-		let b = staking::balance(signed);
-		let candidacy_bond = candidacy_bond();
-		assert!(b >= candidacy_bond);
-		assert!(staking::unlock_block(signed) == staking::LockStatus::Liquid);
+		assert!(!is_a_candidate(signed), "duplicate candidate submission");
+		assert!(staking::internal::deduct_unbonded(signed, candidacy_bond()), "candidate has not enough funds");
 
 		let slot = slot as usize;
 		let count = storage::get_or_default::<u32>(CANDIDATE_COUNT) as usize;
 		let candidates: Vec<AccountId> = storage::get_or_default(CANDIDATES);
 		assert!(
 			(slot == count && count == candidates.len()) ||
-			(slot < candidates.len() && candidates[slot] == AccountId::default())
+			(slot < candidates.len() && candidates[slot] == AccountId::default()),
+			"invalid candidate slot"
 		);
 
-		staking::internal::set_balance(signed, b - candidacy_bond);
-
 		let mut candidates = candidates;
 		if slot == candidates.len() {
 			candidates.push(signed.clone());
@@ -365,20 +358,19 @@ pub mod public {
 	/// Only works if the block number >= current_vote().0 and < current_vote().0 + presentation_duration()
 	/// `signed` should have at least
 	pub fn present(signed: &AccountId, candidate: &AccountId, total: Balance, index: VoteIndex) {
-		assert_eq!(index, vote_index());
+		assert_eq!(index, vote_index(), "index not current");
 		let (_, _, expiring): (BlockNumber, u32, Vec<AccountId>) = storage::get(NEXT_FINALISE)
-			.expect("present can only be called after a tally is started.");
-		let b = staking::balance(signed);
+			.expect("cannot present outside of presentation period");
 		let stakes: Vec<Balance> = storage::get_or_default(SNAPSHOTED_STAKES);
 		let voters: Vec<AccountId> = storage::get_or_default(VOTERS);
 		let bad_presentation_punishment = present_slash_per_voter() * voters.len() as Balance;
-		assert!(b >= bad_presentation_punishment);
+		assert!(staking::can_slash(signed, bad_presentation_punishment), "presenter must have sufficient slashable funds");
 
 		let mut leaderboard = leaderboard().expect("leaderboard must exist while present phase active");
-		assert!(total > leaderboard[0].0);
+		assert!(total > leaderboard[0].0, "candidate not worthy of leaderboard");
 
 		if let Some(p) = active_council().iter().position(|&(ref c, _)| c == candidate) {
-			assert!(p < expiring.len());
+			assert!(p < expiring.len(), "candidate must not form a duplicated member if elected");
 		}
 
 		let (registered_since, candidate_index): (VoteIndex, u32) =
@@ -400,7 +392,7 @@ pub mod public {
 			leaderboard.sort_by_key(|&(t, _)| t);
 			storage::put(LEADERBOARD, &leaderboard);
 		} else {
-			staking::internal::set_balance(signed, b - bad_presentation_punishment);
+			staking::internal::slash(signed, bad_presentation_punishment);
 		}
 	}
 }
@@ -500,6 +492,8 @@ fn finalise_tally() {
 	let leaderboard: Vec<(Balance, AccountId)> = storage::take(LEADERBOARD).unwrap_or_default();
 	let new_expiry = system::block_number() + term_duration();
 
+	println!("Finalising tally {} {}, {:?}", system::block_number(), coming, leaderboard);
+
 	// return bond to winners.
 	let candidacy_bond = candidacy_bond();
 	for &(_, ref w) in leaderboard.iter()
@@ -507,7 +501,9 @@ fn finalise_tally() {
 		.take_while(|&&(b, _)| b != 0)
 		.take(coming as usize)
 	{
-		staking::internal::set_balance(w, staking::balance(w) + candidacy_bond);
+		println!("Refunding by {:?}, {:?}", candidacy_bond, staking::balance(w));
+		staking::internal::refund(w, candidacy_bond);
+		println!("Refunded to {:?}", staking::balance(w));
 	}
 
 	// set the new council.
@@ -690,7 +686,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "invalid candidate slot")]
 	fn candidate_submission_not_using_free_slot_should_panic() {
 		let mut t = new_test_ext_with_candidate_holes();
 
@@ -701,7 +697,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "invalid candidate slot")]
 	fn bad_candidate_slot_submission_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 1);
@@ -711,7 +707,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "invalid candidate slot")]
 	fn non_free_candidate_slot_submission_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 1);
@@ -722,7 +718,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "duplicate candidate submission")]
 	fn dupe_candidate_submission_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 1);
@@ -732,6 +728,16 @@ mod tests {
 		});
 	}
 
+	#[test]
+	#[should_panic(expected = "candidate has not enough funds")]
+	fn poor_candidate_submission_should_panic() {
+		with_externalities(&mut new_test_ext(), || {
+			with_env(|e| e.block_number = 1);
+			assert_eq!(candidates(), Vec::<AccountId>::new());
+			public::submit_candidacy(&One, 0);
+		});
+	}
+
 	#[test]
 	fn voting_should_work() {
 		with_externalities(&mut new_test_ext(), || {
@@ -828,7 +834,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "retraction index mismatch")]
 	fn invalid_retraction_index_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 1);
@@ -840,7 +846,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "retraction index invalid")]
 	fn overflow_retraction_index_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 1);
@@ -851,7 +857,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "cannot retract non-voter")]
 	fn non_voter_retraction_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 1);
@@ -875,9 +881,9 @@ mod tests {
 
 			with_env(|e| e.block_number = 6);
 			assert!(presentation_active());
-			public::present(&Dave, &Bob, 8, 0);
-			public::present(&Dave, &Eve, 38, 0);
-			assert_eq!(leaderboard(), Some(vec![(0, AccountId::default()), (0, AccountId::default()), (8, Bob.into()), (38, Eve.into())]));
+			public::present(&Dave, &Bob, 11, 0);
+			public::present(&Dave, &Eve, 41, 0);
+			assert_eq!(leaderboard(), Some(vec![(0, AccountId::default()), (0, AccountId::default()), (11, Bob.into()), (41, Eve.into())]));
 
 			internal::end_block();
 
@@ -895,6 +901,8 @@ mod tests {
 	#[test]
 	fn double_presentations_should_be_punished() {
 		with_externalities(&mut new_test_ext(), || {
+			assert!(staking::can_slash(&Dave, 10));
+
 			with_env(|e| e.block_number = 4);
 			public::submit_candidacy(&Bob, 0);
 			public::submit_candidacy(&Eve, 1);
@@ -903,9 +911,9 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
-			public::present(&Dave, &Eve, 38, 0);
-			public::present(&Dave, &Eve, 38, 0);
+			public::present(&Dave, &Bob, 11, 0);
+			public::present(&Dave, &Eve, 41, 0);
+			public::present(&Dave, &Eve, 41, 0);
 			internal::end_block();
 
 			assert_eq!(active_council(), vec![(Eve.to_raw_public(), 11), (Bob.into(), 11)]);
@@ -922,7 +930,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
+			public::present(&Dave, &Bob, 11, 0);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 8);
@@ -931,7 +939,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 10);
-			public::present(&Dave, &Eve, 38, 1);
+			public::present(&Dave, &Eve, 41, 1);
 			internal::end_block();
 
 			public::reap_inactive_voter(
@@ -943,12 +951,12 @@ mod tests {
 			assert_eq!(voters(), vec![Eve.to_raw_public()]);
 			assert_eq!(approvals_of(&Bob).len(), 0);
 			assert_eq!(staking::balance(&Bob), 17);
-			assert_eq!(staking::balance(&Eve), 50);
+			assert_eq!(staking::balance(&Eve), 53);
 		});
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "candidate must not form a duplicated member if elected")]
 	fn presenting_for_double_election_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -957,7 +965,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
+			public::present(&Dave, &Bob, 11, 0);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 8);
@@ -966,7 +974,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 10);
-			public::present(&Dave, &Bob, 8, 1);
+			public::present(&Dave, &Bob, 11, 1);
 		});
 	}
 
@@ -979,7 +987,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
+			public::present(&Dave, &Bob, 11, 0);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 8);
@@ -988,7 +996,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 10);
-			public::present(&Dave, &Eve, 38, 1);
+			public::present(&Dave, &Eve, 41, 1);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 11);
@@ -1003,12 +1011,12 @@ mod tests {
 			assert_eq!(voters(), vec![Eve.to_raw_public()]);
 			assert_eq!(approvals_of(&Bob).len(), 0);
 			assert_eq!(staking::balance(&Bob), 17);
-			assert_eq!(staking::balance(&Eve), 50);
+			assert_eq!(staking::balance(&Eve), 53);
 		});
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "bad reporter index")]
 	fn retracting_inactive_voter_with_bad_reporter_index_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1038,7 +1046,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "bad target index")]
 	fn retracting_inactive_voter_with_bad_target_index_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1082,10 +1090,10 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
-			public::present(&Dave, &Charlie, 18, 0);
-			public::present(&Dave, &Dave, 28, 0);
-			public::present(&Dave, &Eve, 38, 0);
+			public::present(&Dave, &Bob, 11, 0);
+			public::present(&Dave, &Charlie, 21, 0);
+			public::present(&Dave, &Dave, 31, 0);
+			public::present(&Dave, &Eve, 41, 0);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 8);
@@ -1093,8 +1101,8 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 10);
-			public::present(&Dave, &Bob, 8, 1);
-			public::present(&Dave, &Charlie, 18, 1);
+			public::present(&Dave, &Bob, 11, 1);
+			public::present(&Dave, &Charlie, 21, 1);
 			internal::end_block();
 
 			public::reap_inactive_voter(
@@ -1110,7 +1118,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "reaper must be a voter")]
 	fn attempting_to_retract_inactive_voter_by_nonvoter_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1119,7 +1127,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
+			public::present(&Dave, &Bob, 11, 0);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 8);
@@ -1128,7 +1136,7 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 10);
-			public::present(&Dave, &Eve, 38, 1);
+			public::present(&Dave, &Eve, 41, 1);
 			internal::end_block();
 
 			public::reap_inactive_voter(
@@ -1140,7 +1148,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "candidate not worthy of leaderboard")]
 	fn presenting_loser_should_panic() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1157,11 +1165,11 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Alice, 57, 0);
-			public::present(&Dave, &Charlie, 18, 0);
-			public::present(&Dave, &Dave, 28, 0);
-			public::present(&Dave, &Eve, 38, 0);
-			public::present(&Dave, &Bob, 8, 0);
+			public::present(&Dave, &Alice, 60, 0);
+			public::present(&Dave, &Charlie, 21, 0);
+			public::present(&Dave, &Dave, 31, 0);
+			public::present(&Dave, &Eve, 41, 0);
+			public::present(&Dave, &Bob, 11, 0);
 		});
 	}
 
@@ -1182,23 +1190,23 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 0);
-			public::present(&Dave, &Alice, 57, 0);
-			public::present(&Dave, &Charlie, 18, 0);
-			public::present(&Dave, &Dave, 28, 0);
-			public::present(&Dave, &Eve, 38, 0);
+			public::present(&Dave, &Bob, 11, 0);
+			public::present(&Dave, &Alice, 60, 0);
+			public::present(&Dave, &Charlie, 21, 0);
+			public::present(&Dave, &Dave, 31, 0);
+			public::present(&Dave, &Eve, 41, 0);
 
 			assert_eq!(leaderboard(), Some(vec![
-				(18, Charlie.into()),
-				(28, Dave.into()),
-				(38, Eve.into()),
-				(57, Alice.to_raw_public())
+				(21, Charlie.into()),
+				(31, Dave.into()),
+				(41, Eve.into()),
+				(60, Alice.to_raw_public())
 			]));
 		});
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "cannot present outside of presentation period")]
 	fn present_panics_outside_of_presentation_period() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1208,7 +1216,7 @@ mod tests {
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "index not current")]
 	fn present_panics_with_invalid_vote_index() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1219,12 +1227,12 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Bob, 8, 1);
+			public::present(&Dave, &Bob, 11, 1);
 		});
 	}
 
 	#[test]
-	#[should_panic]
+	#[should_panic(expected = "presenter must have sufficient slashable funds")]
 	fn present_panics_when_presenter_is_poor() {
 		with_externalities(&mut new_test_ext(), || {
 			with_env(|e| e.block_number = 4);
@@ -1238,7 +1246,7 @@ mod tests {
 
 			with_env(|e| e.block_number = 6);
 			assert_eq!(staking::balance(&Alice), 1);
-			public::present(&Alice, &Alice, 17, 0);
+			public::present(&Alice, &Alice, 30, 0);
 		});
 	}
 
@@ -1283,21 +1291,21 @@ mod tests {
 
 			with_env(|e| e.block_number = 6);
 			assert!(presentation_active());
-			public::present(&Dave, &Alice, 57, 0);
+			public::present(&Dave, &Alice, 60, 0);
 			assert_eq!(leaderboard(), Some(vec![
 				(0, AccountId::default()),
 				(0, AccountId::default()),
 				(0, AccountId::default()),
-				(57, Alice.to_raw_public())
+				(60, Alice.to_raw_public())
 			]));
-			public::present(&Dave, &Charlie, 18, 0);
-			public::present(&Dave, &Dave, 28, 0);
-			public::present(&Dave, &Eve, 38, 0);
+			public::present(&Dave, &Charlie, 21, 0);
+			public::present(&Dave, &Dave, 31, 0);
+			public::present(&Dave, &Eve, 41, 0);
 			assert_eq!(leaderboard(), Some(vec![
-				(18, Charlie.into()),
-				(28, Dave.into()),
-				(38, Eve.into()),
-				(57, Alice.to_raw_public())
+				(21, Charlie.into()),
+				(31, Dave.into()),
+				(41, Eve.into()),
+				(60, Alice.to_raw_public())
 			]));
 
 			internal::end_block();
@@ -1338,10 +1346,10 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 6);
-			public::present(&Dave, &Alice, 57, 0);
-			public::present(&Dave, &Charlie, 18, 0);
-			public::present(&Dave, &Dave, 28, 0);
-			public::present(&Dave, &Eve, 38, 0);
+			public::present(&Dave, &Alice, 60, 0);
+			public::present(&Dave, &Charlie, 21, 0);
+			public::present(&Dave, &Dave, 31, 0);
+			public::present(&Dave, &Eve, 41, 0);
 			internal::end_block();
 
 			with_env(|e| e.block_number = 8);
@@ -1350,8 +1358,8 @@ mod tests {
 			internal::end_block();
 
 			with_env(|e| e.block_number = 10);
-			public::present(&Dave, &Charlie, 75, 1);
-			public::present(&Dave, &Dave, 28, 1);
+			public::present(&Dave, &Charlie, 81, 1);
+			public::present(&Dave, &Dave, 31, 1);
 			internal::end_block();
 
 			assert!(!presentation_active());
diff --git a/substrate/demo/runtime/src/runtime/democracy.rs b/substrate/demo/runtime/src/runtime/democracy.rs
index 044d7c0633959a14d4fd38af3e9b026611caaafa..41c635ddee4c382e199114e807e45c976fc94d03 100644
--- a/substrate/demo/runtime/src/runtime/democracy.rs
+++ b/substrate/demo/runtime/src/runtime/democracy.rs
@@ -178,10 +178,7 @@ pub mod public {
 	/// Propose a sensitive action to be taken.
 	pub fn propose(signed: &AccountId, proposal: &Proposal, value: Balance) {
 		assert!(value >= minimum_deposit());
-		let b = staking::balance(signed);
-		assert!(b >= value);
-
-		staking::internal::set_balance(signed, b - value);
+		assert!(staking::internal::deduct_unbonded(signed, value));
 
 		let index: PropIndex = storage::get_or_default(PUBLIC_PROP_COUNT);
 		storage::put(PUBLIC_PROP_COUNT, &(index + 1));
@@ -194,14 +191,12 @@ pub mod public {
 
 	/// Propose a sensitive action to be taken.
 	pub fn second(signed: &AccountId, proposal: PropIndex) {
-		let b = staking::balance(signed);
 		let key = proposal.to_keyed_vec(DEPOSIT_OF);
 		let mut deposit: (Balance, Vec<AccountId>) =
 			storage::get(&key).expect("can only second an existing proposal");
-		assert!(b >= deposit.0);
-		deposit.1.push(*signed);
+		assert!(staking::internal::deduct_unbonded(signed, deposit.0));
 
-		staking::internal::set_balance(signed, b - deposit.0);
+		deposit.1.push(*signed);
 		storage::put(&key, &deposit);
 	}
 
@@ -258,7 +253,7 @@ pub mod internal {
 						.expect("depositors always exist for current proposals");
 				// refund depositors
 				for d in &depositors {
-					staking::internal::set_balance(d, staking::balance(d) + deposit);
+					staking::internal::refund(d, deposit);
 				}
 				storage::put(PUBLIC_PROPS, &public_props);
 				inject_referendum(now + voting_period(), proposal, VoteThreshold::SuperMajorityApprove);
diff --git a/substrate/demo/runtime/src/runtime/staking.rs b/substrate/demo/runtime/src/runtime/staking.rs
index 29b6ab793b296febd103aacb4d9620cf28fdb898..fea37d938359a6525135fac95b362044771eba8d 100644
--- a/substrate/demo/runtime/src/runtime/staking.rs
+++ b/substrate/demo/runtime/src/runtime/staking.rs
@@ -17,6 +17,7 @@
 //! Staking manager: Handles balances and periodically determines the best set of validators.
 
 use rstd::prelude::*;
+use rstd::cmp;
 use rstd::cell::RefCell;
 use rstd::collections::btree_map::{BTreeMap, Entry};
 use runtime_io::{print, blake2_256};
@@ -42,6 +43,7 @@ pub const INTENTION_AT: &[u8] = b"sta:wil:";
 pub const INTENTION_COUNT: &[u8] = b"sta:wil:len";
 
 pub const BALANCE_OF: &[u8] = b"sta:bal:";
+pub const RESERVED_BALANCE_OF: &[u8] = b"sta:lbo:";
 pub const BONDAGE_OF: &[u8] = b"sta:bon:";
 pub const CODE_OF: &[u8] = b"sta:cod:";
 pub const STORAGE_OF: &[u8] = b"sta:sto:";
@@ -84,9 +86,26 @@ pub fn last_era_length_change() -> BlockNumber {
 
 /// The balance of a given account.
 pub fn balance(who: &AccountId) -> Balance {
+	free_balance(who) + reserved_balance(who)
+}
+
+/// The balance of a given account.
+pub fn free_balance(who: &AccountId) -> Balance {
 	storage::get_or_default(&who.to_keyed_vec(BALANCE_OF))
 }
 
+/// The amount of the balance of a given account that is exterally reserved; this can still get
+/// slashed, but gets slashed last of all.
+pub fn reserved_balance(who: &AccountId) -> Balance {
+	storage::get_or_default(&who.to_keyed_vec(RESERVED_BALANCE_OF))
+}
+
+/// Some result as `slash(who, value)` (but without the side-effects) asuming there are no
+/// balance changes in the meantime.
+pub fn can_slash(who: &AccountId, value: Balance) -> bool {
+	balance(who) >= value
+}
+
 /// The block at which the `who`'s funds become entirely liquid.
 pub fn bondage(who: &AccountId) -> Bondage {
 	storage::get_or_default(&who.to_keyed_vec(BONDAGE_OF))
@@ -388,7 +407,7 @@ pub mod internal {
 	/// Set the balance of an account.
 	/// Needless to say, this is super low-level and accordingly dangerous. Ensure any modules that
 	/// use it are auditted to the hilt.
-	pub fn set_balance(who: &AccountId, value: Balance) {
+	pub fn set_free_balance(who: &AccountId, value: Balance) {
 		storage::put(&who.to_keyed_vec(BALANCE_OF), &value);
 	}
 
@@ -399,6 +418,72 @@ pub mod internal {
 			new_era();
 		}
 	}
+
+	/// Deduct from an unbonded balance. true if it happened.
+	pub fn deduct_unbonded(who: &AccountId, value: Balance) -> bool {
+		if let LockStatus::Liquid = unlock_block(who) {
+			let b = free_balance(who);
+			if b >= value {
+				set_free_balance(who, b - value);
+				return true;
+			}
+		}
+		false
+	}
+
+	/// Refund some balance.
+	pub fn refund(who: &AccountId, value: Balance) {
+		set_free_balance(who, free_balance(who) + value)
+	}
+
+	/// Will slash any balance, but prefer free over reserved.
+	pub fn slash(who: &AccountId, value: Balance) -> bool {
+		let free_balance = free_balance(who);
+		let free_slash = cmp::min(free_balance, value);
+		set_free_balance(who, free_balance - free_slash);
+		if free_slash < value {
+			slash_reserved(who, value - free_slash)
+		} else {
+			true
+		}
+	}
+
+	/// Moves `value` from balance to reserved balance.
+	pub fn reserve_balance(who: &AccountId, value: Balance) {
+		let b = free_balance(who);
+		assert!(b >= value);
+		set_free_balance(who, b - value);
+		set_reserved_balance(who, reserved_balance(who) + value);
+	}
+
+	/// Moves `value` from reserved balance to balance.
+	pub fn unreserve_balance(who: &AccountId, value: Balance) {
+		let b = reserved_balance(who);
+		let value = cmp::min(b, value);
+		set_reserved_balance(who, b - value);
+		set_free_balance(who, free_balance(who) + value);
+	}
+
+	/// Moves `value` from reserved balance to balance.
+	pub fn slash_reserved(who: &AccountId, value: Balance) -> bool {
+		let b = reserved_balance(who);
+		let slash = cmp::min(b, value);
+		set_reserved_balance(who, b - slash);
+		value == slash
+	}
+
+	/// Moves `value` from reserved balance to balance.
+	pub fn transfer_reserved_balance(slashed: &AccountId, beneficiary: &AccountId, value: Balance) {
+		let b = reserved_balance(slashed);
+		let value = cmp::min(b, value);
+		set_reserved_balance(slashed, b - value);
+		set_free_balance(beneficiary, free_balance(beneficiary) + value);
+	}
+}
+
+/// Set the reserved portion of `who`'s balance.
+fn set_reserved_balance(who: &AccountId, value: Balance) {
+	storage::put(&who.to_keyed_vec(RESERVED_BALANCE_OF), &value);
 }
 
 /// The era has changed - enact new staking set.