diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 23cd7405dda6055a38d0f5d6581a22f755350ce7..7e1082ef546bb0e08e7615c5fcf7c022fb6a47ed 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
 	// implementation changes and behavior does not, then leave spec_version as
 	// is and increment impl_version.
 	spec_version: 205,
-	impl_version: 205,
+	impl_version: 206,
 	apis: RUNTIME_API_VERSIONS,
 };
 
diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs
index ceaccd35cb6ba34d126fa497affcce22a7600cb2..cfbefa2a72c93a2e2402b7359c5b733f7eafd39f 100644
--- a/substrate/frame/contracts/src/exec.rs
+++ b/substrate/frame/contracts/src/exec.rs
@@ -356,10 +356,10 @@ where
 			});
 		}
 
-		// Assumption: pay_rent doesn't collide with overlay because
-		// pay_rent will be done on first call and dest contract and balance
+		// Assumption: `collect_rent` doesn't collide with overlay because
+		// `collect_rent` will be done on first call and destination contract and balance
 		// cannot be changed before the first call
-		let contract_info = rent::pay_rent::<T>(&dest);
+		let contract_info = rent::collect_rent::<T>(&dest);
 
 		// Calls to dead contracts always fail.
 		if let Some(ContractInfo::Tombstone(_)) = contract_info {
diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs
index d16462d8bf5c1b92c203a224d18579687999ac37..1460ca3cf1b14877f936f6ccbf103e0149d7df61 100644
--- a/substrate/frame/contracts/src/lib.rs
+++ b/substrate/frame/contracts/src/lib.rs
@@ -669,7 +669,7 @@ decl_module! {
 			};
 
 			// If poking the contract has lead to eviction of the contract, give out the rewards.
-			if rent::try_evict::<T>(&dest, handicap) == rent::RentOutcome::Evicted {
+			if rent::snitch_contract_should_be_evicted::<T>(&dest, handicap) {
 				T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?;
 			}
 		}
diff --git a/substrate/frame/contracts/src/rent.rs b/substrate/frame/contracts/src/rent.rs
index 508511da4cbe3b00d96b85fc444296906493404d..46f915e64264f839c38a2fe2bda8333e0de43356 100644
--- a/substrate/frame/contracts/src/rent.rs
+++ b/substrate/frame/contracts/src/rent.rs
@@ -14,64 +14,91 @@
 // You should have received a copy of the GNU General Public License
 // along with Substrate. If not, see <http://www.gnu.org/licenses/>.
 
-use crate::{Module, RawEvent, BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo,
-	Trait, AliveContractInfo};
-use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero,
-	SaturatedConversion};
-use frame_support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason, OnUnbalanced};
-use frame_support::StorageMap;
+use crate::{
+	AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Module, RawEvent,
+	TombstoneContractInfo, Trait,
+};
 use frame_support::storage::child;
+use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason};
+use frame_support::StorageMap;
+use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero};
 
-#[derive(PartialEq, Eq, Copy, Clone)]
-#[must_use]
-pub enum RentOutcome {
-	/// Exempted from rent iff:
-	/// * rent is offset completely by the `rent_deposit_offset`,
-	/// * or rent has already been paid for this block number,
-	/// * or account doesn't have a contract,
-	/// * or account has a tombstone.
-	Exempted,
-	/// Evicted iff:
-	/// * rent exceed rent allowance,
-	/// * or can't withdraw the rent,
-	/// * or go below subsistence threshold.
-	Evicted,
-	/// The outstanding dues were paid or were able to be paid.
-	Ok,
+/// The amount to charge.
+///
+/// This amount respects the contract's rent allowance and the subsistence deposit.
+/// Because of that, charging the amount cannot remove the contract.
+struct OutstandingAmount<T: Trait> {
+	amount: BalanceOf<T>,
 }
 
-/// Evict and optionally pay dues (or check account can pay them otherwise) at the current
-/// block number (modulo `handicap`, read on).
-///
-/// `pay_rent` gives an ability to pay or skip paying rent.
-/// `handicap` gives a way to check or pay the rent up to a moment in the past instead
-/// of current block.
+impl<T: Trait> OutstandingAmount<T> {
+	/// Create the new outstanding amount.
+	///
+	/// The amount should be always withdrawable and it should not kill the account.
+	fn new(amount: BalanceOf<T>) -> Self {
+		Self { amount }
+	}
+
+	/// Returns the amount this instance wraps.
+	fn peek(&self) -> BalanceOf<T> {
+		self.amount
+	}
+
+	/// Withdraws the outstanding amount from the given account.
+	fn withdraw(self, account: &T::AccountId) {
+		if let Ok(imbalance) = T::Currency::withdraw(
+			account,
+			self.amount,
+			WithdrawReason::Fee.into(),
+			ExistenceRequirement::KeepAlive,
+		) {
+			// This should never fail. However, let's err on the safe side.
+			T::RentPayment::on_unbalanced(imbalance);
+		}
+	}
+}
+
+enum Verdict<T: Trait> {
+	/// The contract is exempted from paying rent.
+	///
+	/// For example, it already paid its rent in the current block, or it has enough deposit for not
+	/// paying rent at all.
+	Exempt,
+	/// Funds dropped below the subsistence deposit.
+	///
+	/// Remove the contract along with it's storage.
+	Kill,
+	/// The contract cannot afford payment within its rent budget so it gets evicted. However,
+	/// because its balance is greater than the subsistence threshold it leaves a tombstone.
+	Evict {
+		amount: Option<OutstandingAmount<T>>,
+	},
+	/// Everything is OK, we just only take some charge.
+	Charge {
+		amount: OutstandingAmount<T>,
+	},
+}
+
+/// Consider the case for rent payment of the given account and returns a `Verdict`.
 ///
-/// NOTE: This function acts eagerly, all modification are committed into the storage.
-fn try_evict_or_and_pay_rent<T: Trait>(
+/// The `current_block_number` must be equal to the current block number. Use `handicap` do
+/// change the reference block number. (See `snitch_contract_should_be_evicted` for more details).
+fn consider_case<T: Trait>(
 	account: &T::AccountId,
+	current_block_number: T::BlockNumber,
 	handicap: T::BlockNumber,
-	pay_rent: bool,
-) -> (RentOutcome, Option<ContractInfo<T>>) {
-	let contract_info = <ContractInfoOf<T>>::get(account);
-	let contract = match contract_info {
-		None | Some(ContractInfo::Tombstone(_)) => return (RentOutcome::Exempted, contract_info),
-		Some(ContractInfo::Alive(contract)) => contract,
-	};
-
-	let current_block_number = <frame_system::Module<T>>::block_number();
-
+	contract: &AliveContractInfo<T>,
+) -> Verdict<T> {
 	// How much block has passed since the last deduction for the contract.
 	let blocks_passed = {
 		// Calculate an effective block number, i.e. after adjusting for handicap.
 		let effective_block_number = current_block_number.saturating_sub(handicap);
-		let n = effective_block_number.saturating_sub(contract.deduct_block);
-		if n.is_zero() {
-			// Rent has already been paid
-			return (RentOutcome::Exempted, Some(ContractInfo::Alive(contract)));
-		}
-		n
+		effective_block_number.saturating_sub(contract.deduct_block)
 	};
+	if blocks_passed.is_zero() {
+		// Rent has already been paid
+		return Verdict::Exempt;
+	}
 
 	let balance = T::Currency::free_balance(account);
 
@@ -92,7 +119,7 @@ fn try_evict_or_and_pay_rent<T: Trait>(
 	if fee_per_block.is_zero() {
 		// The rent deposit offset reduced the fee to 0. This means that the contract
 		// gets the rent for free.
-		return (RentOutcome::Exempted, Some(ContractInfo::Alive(contract)));
+		return Verdict::Exempt;
 	}
 
 	// The minimal amount of funds required for a contract not to be evicted.
@@ -100,10 +127,7 @@ fn try_evict_or_and_pay_rent<T: Trait>(
 
 	if balance < subsistence_threshold {
 		// The contract cannot afford to leave a tombstone, so remove the contract info altogether.
-		<ContractInfoOf<T>>::remove(account);
-		child::kill_storage(&contract.trie_id, contract.child_trie_unique_id());
-		<Module<T>>::deposit_event(RawEvent::Evicted(account.clone(), false));
-		return (RentOutcome::Evicted, None);
+		return Verdict::Kill;
 	}
 
 	let dues = fee_per_block
@@ -127,75 +151,136 @@ fn try_evict_or_and_pay_rent<T: Trait>(
 	)
 	.is_ok();
 
-	if can_withdraw_rent && (insufficient_rent || pay_rent) {
-		// Collect dues.
-		let imbalance = T::Currency::withdraw(
-			account,
-			dues_limited,
-			WithdrawReason::Fee.into(),
-			ExistenceRequirement::KeepAlive,
-		)
-		.expect(
-			"Withdraw has been checked above;
-			dues_limited < rent_budget < balance - subsistence < balance - existential_deposit;
-			qed",
-		);
-
-		T::RentPayment::on_unbalanced(imbalance);
-	}
-
 	if insufficient_rent || !can_withdraw_rent {
 		// The contract cannot afford the rent payment and has a balance above the subsistence
 		// threshold, so it leaves a tombstone.
+		let amount = if can_withdraw_rent {
+			Some(OutstandingAmount::new(dues_limited))
+		} else {
+			None
+		};
+		return Verdict::Evict { amount };
+	}
 
-		// Note: this operation is heavy.
-		let child_storage_root = child::child_root(
-			&contract.trie_id,
-		);
+	return Verdict::Charge {
+		// We choose to use `dues_limited` here instead of `dues` just to err on the safer side.
+		amount: OutstandingAmount::new(dues_limited),
+	};
+}
 
-		let tombstone = <TombstoneContractInfo<T>>::new(
-			&child_storage_root[..],
-			contract.code_hash,
-		);
-		let tombstone_info = ContractInfo::Tombstone(tombstone);
-		<ContractInfoOf<T>>::insert(account, &tombstone_info);
+/// Enacts the given verdict and returns the updated `ContractInfo`.
+///
+/// `alive_contract_info` should be from the same address as `account`.
+fn enact_verdict<T: Trait>(
+	account: &T::AccountId,
+	alive_contract_info: AliveContractInfo<T>,
+	current_block_number: T::BlockNumber,
+	verdict: Verdict<T>,
+) -> Option<ContractInfo<T>> {
+	match verdict {
+		Verdict::Exempt => return Some(ContractInfo::Alive(alive_contract_info)),
+		Verdict::Kill => {
+			<ContractInfoOf<T>>::remove(account);
+			child::kill_storage(
+				&alive_contract_info.trie_id,
+				alive_contract_info.child_trie_unique_id(),
+			);
+			<Module<T>>::deposit_event(RawEvent::Evicted(account.clone(), false));
+			None
+		}
+		Verdict::Evict { amount } => {
+			if let Some(amount) = amount {
+				amount.withdraw(account);
+			}
 
-		child::kill_storage(&contract.trie_id, contract.child_trie_unique_id());
+			// Note: this operation is heavy.
+			let child_storage_root = child::child_root(&alive_contract_info.trie_id);
 
-		<Module<T>>::deposit_event(RawEvent::Evicted(account.clone(), true));
+			let tombstone = <TombstoneContractInfo<T>>::new(
+				&child_storage_root[..],
+				alive_contract_info.code_hash,
+			);
+			let tombstone_info = ContractInfo::Tombstone(tombstone);
+			<ContractInfoOf<T>>::insert(account, &tombstone_info);
 
-		return (RentOutcome::Evicted, Some(tombstone_info));
-	}
+			child::kill_storage(
+				&alive_contract_info.trie_id,
+				alive_contract_info.child_trie_unique_id(),
+			);
 
-	if pay_rent {
-		let contract_info = ContractInfo::Alive(AliveContractInfo::<T> {
-			rent_allowance: contract.rent_allowance - dues, // rent_allowance is not exceeded
-			deduct_block: current_block_number,
-			..contract
-		});
-
-		<ContractInfoOf<T>>::insert(account, &contract_info);
+			<Module<T>>::deposit_event(RawEvent::Evicted(account.clone(), true));
+			Some(tombstone_info)
+		}
+		Verdict::Charge { amount } => {
+			let contract_info = ContractInfo::Alive(AliveContractInfo::<T> {
+				rent_allowance: alive_contract_info.rent_allowance - amount.peek(),
+				deduct_block: current_block_number,
+				..alive_contract_info
+			});
+			<ContractInfoOf<T>>::insert(account, &contract_info);
 
-		return (RentOutcome::Ok, Some(contract_info));
+			amount.withdraw(account);
+			Some(contract_info)
+		}
 	}
-
-	(RentOutcome::Ok, Some(ContractInfo::Alive(contract)))
 }
 
 /// Make account paying the rent for the current block number
 ///
-/// NOTE: This function acts eagerly.
-pub fn pay_rent<T: Trait>(account: &T::AccountId) -> Option<ContractInfo<T>> {
-	try_evict_or_and_pay_rent::<T>(account, Zero::zero(), true).1
+/// NOTE this function performs eviction eagerly. All changes are read and written directly to
+/// storage.
+pub fn collect_rent<T: Trait>(account: &T::AccountId) -> Option<ContractInfo<T>> {
+	let contract_info = <ContractInfoOf<T>>::get(account);
+	let alive_contract_info = match contract_info {
+		None | Some(ContractInfo::Tombstone(_)) => return contract_info,
+		Some(ContractInfo::Alive(contract)) => contract,
+	};
+
+	let current_block_number = <frame_system::Module<T>>::block_number();
+	let verdict = consider_case::<T>(
+		account,
+		current_block_number,
+		Zero::zero(),
+		&alive_contract_info,
+	);
+	enact_verdict(account, alive_contract_info, current_block_number, verdict)
 }
 
-/// Evict the account if it should be evicted at the given block number.
+/// Process a snitch that a contract under the given address should be evicted.
+///
+/// Enact the eviction right away if the contract should be evicted and return true.
+/// Otherwise, **do nothing** and return false.
 ///
-/// `handicap` gives a way to check or pay the rent up to a moment in the past instead
+/// The `handicap` parameter gives a way to check the rent to a moment in the past instead
 /// of current block. E.g. if the contract is going to be evicted at the current block,
-/// `handicap=1` can defer the eviction for 1 block.
+/// `handicap = 1` can defer the eviction for 1 block. This is useful to handicap certain snitchers
+/// relative to others.
 ///
-/// NOTE: This function acts eagerly.
-pub fn try_evict<T: Trait>(account: &T::AccountId, handicap: T::BlockNumber) -> RentOutcome {
-	try_evict_or_and_pay_rent::<T>(account, handicap, false).0
+/// NOTE this function performs eviction eagerly. All changes are read and written directly to
+/// storage.
+pub fn snitch_contract_should_be_evicted<T: Trait>(
+	account: &T::AccountId,
+	handicap: T::BlockNumber,
+) -> bool {
+	let contract_info = <ContractInfoOf<T>>::get(account);
+	let alive_contract_info = match contract_info {
+		None | Some(ContractInfo::Tombstone(_)) => return false,
+		Some(ContractInfo::Alive(contract)) => contract,
+	};
+	let current_block_number = <frame_system::Module<T>>::block_number();
+	let verdict = consider_case::<T>(
+		account,
+		current_block_number,
+		handicap,
+		&alive_contract_info,
+	);
+
+	// Enact the verdict only if the contract gets removed.
+	match verdict {
+		Verdict::Kill | Verdict::Evict { .. } => {
+			enact_verdict(account, alive_contract_info, current_block_number, verdict);
+			true
+		}
+		_ => false,
+	}
 }