Repository: paritytech/substrate
File: frame/contracts/src/storage/meter.rs (Rust)

Code before:

		// A consumer is added at account creation and removed it on termination, otherwise the
		// runtime could remove the account. As long as a contract exists its account must exist.
		// With the consumer, a correct runtime cannot remove the account.
		System::<T>::inc_consumers(contract)?;

		// Normally, deposit charges are deferred to be able to coalesce them with refunds.
		// However, we need to charge immediately so that the account is created before
		// charges possibly below the ed are collected and fail.
		E::charge(
			origin,
			contract,
			&deposit.saturating_sub(&Deposit::Charge(ed)),
			&ContractStatus::Alive,
		)?;

		Ok(deposit)
	}

	/// Call to tell the meter that the currently executing contract was executed.
	///
	/// This will manipulate the meter so that all storage deposit accumulated in
	/// `contract_info` will be refunded to the `origin` of the meter. And the free
	/// (`reducible_balance`) will be sent to the `beneficiary`.
	pub fn terminate(&mut self, info: &ContractInfo<T>, beneficiary: T::AccountId) {
		debug_assert!(self.is_alive());
		self.own_contribution = Contribution::Terminated {
			deposit: Deposit::Refund(info.total_deposit()),
			beneficiary,
		};
	}

	/// [`Self::charge`] does not enforce the storage limit since we want to do this check as late
	/// as possible to allow later refunds to offset earlier charges.
	///
	/// # Note
	///
	/// We normally need to call this **once** for every call stack and not for every cross contract
	/// call. However, if a dedicated limit is specified for a sub-call, this needs to be called
	/// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is
	/// used.
	pub fn enforce_limit(
		&mut self,
		info: Option<&mut ContractInfo<T>>,
	) -> Result<(), DispatchError> {
		let deposit = self.own_contribution.update_contract(info);
		let total_deposit = self.total_deposit.saturating_add(&deposit);
		// We don't want to override a `Terminated` with a `Checked`.
		if self.is_alive() {
			self.own_contribution = Contribution::Checked(deposit);
		}

Review comment (bug):
```suggestion
		debug_assert!(matches!(self.contract_state(), ContractState::Alive(_))));
```

Code after:

		// Normally, deposit charges are deferred to be able to coalesce them with refunds.
		// However, we need to charge immediately so that the account is created before
		// charges possibly below the ed are collected and fail.
		E::charge(
			origin,
			contract,
			&deposit.saturating_sub(&Deposit::Charge(ed)),
			&ContractState::Alive,
		)?;

		Ok(deposit)
	}

	/// Call to tell the meter that the currently executing contract was terminated.
	///
	/// This will manipulate the meter so that all storage deposit accumulated in
	/// `contract_info` will be refunded to the `origin` of the meter. And the free
	/// (`reducible_balance`) will be sent to the `beneficiary`.
	pub fn terminate(&mut self, info: &ContractInfo<T>, beneficiary: T::AccountId) {
		debug_assert!(matches!(self.contract_state(), ContractState::Alive));
		self.own_contribution = Contribution::Terminated {
			deposit: Deposit::Refund(info.total_deposit()),
			beneficiary,
		};
	}

	/// [`Self::charge`] does not enforce the storage limit since we want to do this check as late
	/// as possible to allow later refunds to offset earlier charges.
	///
	/// # Note
	///
	/// We normally need to call this **once** for every call stack and not for every cross contract
	/// call. However, if a dedicated limit is specified for a sub-call, this needs to be called
	/// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is
	/// used.
	pub fn enforce_limit(
		&mut self,
		info: Option<&mut ContractInfo<T>>,
	) -> Result<(), DispatchError> {
		let deposit = self.own_contribution.update_contract(info);
		let total_deposit = self.total_deposit.saturating_add(&deposit);
		// We don't want to override a `Terminated` with a `Checked`.
		if matches!(self.contract_state(), ContractState::Alive) {
			self.own_contribution = Contribution::Checked(deposit);
		}
		if let Deposit::Charge(amount) = total_deposit {
			if amount > self.limit {
				return Err(<Error<T>>::StorageDepositLimitExhausted.into())
			}
		}