// This file is part of Substrate. // Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! # Contract Module //! //! The Contract module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts. //! //! - [`contract::Config`](./trait.Config.html) //! - [`Call`](./enum.Call.html) //! //! ## Overview //! //! This module extends accounts based on the `Currency` trait to have smart-contract functionality. It can //! be used with other modules that implement accounts based on `Currency`. These "smart-contract accounts" //! have the ability to instantiate smart-contracts and make calls to other contract and non-contract accounts. //! //! The smart-contract code is stored once in a `code_cache`, and later retrievable via its `code_hash`. //! This means that multiple smart-contracts can be instantiated from the same `code_cache`, without replicating //! the code each time. //! //! When a smart-contract is called, its associated code is retrieved via the code hash and gets executed. //! This call can alter the storage entries of the smart-contract account, instantiate new smart-contracts, //! or call other smart-contracts. //! //! Finally, when an account is reaped, its associated code and storage of the smart-contract account //! will also be deleted. //! //! ### Gas //! //! Senders must specify a gas limit with every call, as all instructions invoked by the smart-contract require gas. //! Unused gas is refunded after the call, regardless of the execution outcome. //! //! If the gas limit is reached, then all calls and state changes (including balance transfers) are only //! reverted at the current call's contract level. For example, if contract A calls B and B runs out of gas mid-call, //! then all of B's calls are reverted. Assuming correct error handling by contract A, A's other calls and state //! changes still persist. //! //! ### Notable Scenarios //! //! Contract call failures are not always cascading. When failures occur in a sub-call, they do not "bubble up", //! and the call will only revert at the specific contract level. For example, if contract A calls contract B, and B //! fails, A can decide how to handle that failure, either proceeding or reverting A's changes. //! //! ## Interface //! //! ### Dispatchable functions //! //! * `put_code` - Stores the given binary Wasm code into the chain's storage and returns its `code_hash`. //! * `instantiate` - Deploys a new contract from the given `code_hash`, optionally transferring some balance. //! This instantiates a new smart contract account and calls its contract deploy handler to //! initialize the contract. //! * `call` - Makes a call to an account, optionally transferring some balance. //! //! ## Usage //! //! The Contract module is a work in progress. The following examples show how this Contract module //! can be used to instantiate and call contracts. //! //! * [`ink`](https://github.com/paritytech/ink) is //! an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing //! WebAssembly based smart contracts in the Rust programming language. This is a work in progress. //! //! ## Related Modules //! //! * [Balances](../pallet_balances/index.html) #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "runtime-benchmarks", recursion_limit="256")] #[macro_use] mod gas; mod storage; mod exec; mod wasm; mod rent; mod benchmarking; mod schedule; pub mod weights; #[cfg(test)] mod tests; pub use crate::{ gas::{Gas, GasMeter}, wasm::ReturnCode as RuntimeReturnCode, weights::WeightInfo, schedule::{Schedule, HostFnWeights, InstructionWeights, Limits}, }; use crate::{ exec::ExecutionContext, wasm::{WasmLoader, WasmVm}, rent::Rent, storage::Storage, }; use sp_core::crypto::UncheckedFrom; use sp_std::{prelude::*, marker::PhantomData, fmt::Debug}; use codec::{Codec, Encode, Decode}; use sp_runtime::{ traits::{ Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, Convert, Saturating, }, RuntimeDebug, }; use frame_support::{ decl_module, decl_event, decl_storage, decl_error, ensure, storage::child::ChildInfo, dispatch::{DispatchResult, DispatchResultWithPostInfo}, traits::{OnUnbalanced, Currency, Get, Time, Randomness}, }; use frame_system::{ensure_signed, ensure_root}; use pallet_contracts_primitives::{ RentProjectionResult, GetStorageResult, ContractAccessError, ContractExecResult, ExecResult, }; use frame_support::weights::Weight; pub type CodeHash = ::Hash; pub type TrieId = Vec; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account #[derive(Encode, Decode, RuntimeDebug)] pub enum ContractInfo { Alive(AliveContractInfo), Tombstone(TombstoneContractInfo), } impl ContractInfo { /// If contract is alive then return some alive info pub fn get_alive(self) -> Option> { if let ContractInfo::Alive(alive) = self { Some(alive) } else { None } } /// If contract is alive then return some reference to alive info pub fn as_alive(&self) -> Option<&AliveContractInfo> { if let ContractInfo::Alive(ref alive) = self { Some(alive) } else { None } } /// If contract is alive then return some mutable reference to alive info pub fn as_alive_mut(&mut self) -> Option<&mut AliveContractInfo> { if let ContractInfo::Alive(ref mut alive) = self { Some(alive) } else { None } } /// If contract is tombstone then return some tombstone info pub fn get_tombstone(self) -> Option> { if let ContractInfo::Tombstone(tombstone) = self { Some(tombstone) } else { None } } /// If contract is tombstone then return some reference to tombstone info pub fn as_tombstone(&self) -> Option<&TombstoneContractInfo> { if let ContractInfo::Tombstone(ref tombstone) = self { Some(tombstone) } else { None } } /// If contract is tombstone then return some mutable reference to tombstone info pub fn as_tombstone_mut(&mut self) -> Option<&mut TombstoneContractInfo> { if let ContractInfo::Tombstone(ref mut tombstone) = self { Some(tombstone) } else { None } } } pub type AliveContractInfo = RawAliveContractInfo, BalanceOf, ::BlockNumber>; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct RawAliveContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, /// The total number of bytes used by this contract. /// /// It is a sum of each key-value pair stored by this contract. pub storage_size: u32, /// The number of key-value pairs that have values of zero length. /// The condition `empty_pair_count ≤ total_pair_count` always holds. pub empty_pair_count: u32, /// The total number of key-value pairs in storage of this contract. pub total_pair_count: u32, /// The code associated with a given account. pub code_hash: CodeHash, /// Pay rent at most up to this value. pub rent_allowance: Balance, /// Last block rent has been payed. pub deduct_block: BlockNumber, /// Last block child storage has been written. pub last_write: Option, } impl RawAliveContractInfo { /// Associated child trie unique id is built from the hash part of the trie id. pub fn child_trie_info(&self) -> ChildInfo { child_trie_info(&self.trie_id[..]) } } /// Associated child trie unique id is built from the hash part of the trie id. pub(crate) fn child_trie_info(trie_id: &[u8]) -> ChildInfo { ChildInfo::new_default(trie_id) } pub type TombstoneContractInfo = RawTombstoneContractInfo<::Hash, ::Hashing>; #[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)] pub struct RawTombstoneContractInfo(H, PhantomData); impl RawTombstoneContractInfo where H: Member + MaybeSerializeDeserialize+ Debug + AsRef<[u8]> + AsMut<[u8]> + Copy + Default + sp_std::hash::Hash + Codec, Hasher: Hash, { fn new(storage_root: &[u8], code_hash: H) -> Self { let mut buf = Vec::new(); storage_root.using_encoded(|encoded| buf.extend_from_slice(encoded)); buf.extend_from_slice(code_hash.as_ref()); RawTombstoneContractInfo(::hash(&buf[..]), PhantomData) } } impl From> for ContractInfo { fn from(alive_info: AliveContractInfo) -> Self { Self::Alive(alive_info) } } pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; pub trait Config: frame_system::Config { type Time: Time; type Randomness: Randomness; /// The currency in which fees are paid and contract balances are held. type Currency: Currency; /// The overarching event type. type Event: From> + Into<::Event>; /// Handler for rent payments. type RentPayment: OnUnbalanced>; /// Number of block delay an extrinsic claim surcharge has. /// /// When claim surcharge is called by an extrinsic the rent is checked /// for current_block - delay type SignedClaimHandicap: Get; /// The minimum amount required to generate a tombstone. type TombstoneDeposit: Get>; /// A size offset for an contract. A just created account with untouched storage will have that /// much of storage from the perspective of the state rent. /// /// This is a simple way to ensure that contracts with empty storage eventually get deleted by /// making them pay rent. This creates an incentive to remove them early in order to save rent. type StorageSizeOffset: Get; /// Price of a byte of storage per one block interval. Should be greater than 0. type RentByteFee: Get>; /// The amount of funds a contract should deposit in order to offset /// the cost of one byte. /// /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day, /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent. /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000, /// then it would pay 500 BU/day. type RentDepositOffset: Get>; /// Reward that is received by the party whose touch has led /// to removal of a contract. type SurchargeReward: Get>; /// The maximum nesting level of a call/instantiate stack. type MaxDepth: Get; /// The maximum size of a storage value and event payload in bytes. type MaxValueSize: Get; /// Used to answer contracts's queries regarding the current weight price. This is **not** /// used to calculate the actual fee and is only for informational purposes. type WeightPrice: Convert>; /// Describes the weights of the dispatchables of this module and is also used to /// construct a default cost schedule. type WeightInfo: WeightInfo; } decl_error! { /// Error for the contracts module. pub enum Error for Module where T::AccountId: UncheckedFrom, T::AccountId: AsRef<[u8]>, { /// A new schedule must have a greater version than the current one. InvalidScheduleVersion, /// An origin must be signed or inherent and auxiliary sender only provided on inherent. InvalidSurchargeClaim, /// Cannot restore from nonexisting or tombstone contract. InvalidSourceContract, /// Cannot restore to nonexisting or alive contract. InvalidDestinationContract, /// Tombstones don't match. InvalidTombstone, /// An origin TrieId written in the current block. InvalidContractOrigin, /// The executed contract exhausted its gas limit. OutOfGas, /// The output buffer supplied to a contract API call was too small. OutputBufferTooSmall, /// Performing the requested transfer would have brought the contract below /// the subsistence threshold. No transfer is allowed to do this in order to allow /// for a tombstone to be created. Use `seal_terminate` to remove a contract without /// leaving a tombstone behind. BelowSubsistenceThreshold, /// The newly created contract is below the subsistence threshold after executing /// its contructor. No contracts are allowed to exist below that threshold. NewContractNotFunded, /// Performing the requested transfer failed for a reason originating in the /// chosen currency implementation of the runtime. Most probably the balance is /// too low or locks are placed on it. TransferFailed, /// Performing a call was denied because the calling depth reached the limit /// of what is specified in the schedule. MaxCallDepthReached, /// The contract that was called is either no contract at all (a plain account) /// or is a tombstone. NotCallable, /// The code supplied to `put_code` exceeds the limit specified in the current schedule. CodeTooLarge, /// No code could be found at the supplied code hash. CodeNotFound, /// A buffer outside of sandbox memory was passed to a contract API function. OutOfBounds, /// Input passed to a contract API function failed to decode as expected type. DecodingFailed, /// Contract trapped during execution. ContractTrapped, /// The size defined in `T::MaxValueSize` was exceeded. ValueTooLarge, /// The action performed is not allowed while the contract performing it is already /// on the call stack. Those actions are contract self destruction and restoration /// of a tombstone. ReentranceDenied, /// `seal_input` was called twice from the same contract execution context. InputAlreadyRead, /// The subject passed to `seal_random` exceeds the limit. RandomSubjectTooLong, /// The amount of topics passed to `seal_deposit_events` exceeds the limit. TooManyTopics, /// The topics passed to `seal_deposit_events` contains at least one duplicate. DuplicateTopics, } } decl_module! { /// Contracts module. pub struct Module for enum Call where origin: T::Origin, T::AccountId: UncheckedFrom, T::AccountId: AsRef<[u8]>, { type Error = Error; /// Number of block delay an extrinsic claim surcharge has. /// /// When claim surcharge is called by an extrinsic the rent is checked /// for current_block - delay const SignedClaimHandicap: T::BlockNumber = T::SignedClaimHandicap::get(); /// The minimum amount required to generate a tombstone. const TombstoneDeposit: BalanceOf = T::TombstoneDeposit::get(); /// A size offset for an contract. A just created account with untouched storage will have that /// much of storage from the perspective of the state rent. /// /// This is a simple way to ensure that contracts with empty storage eventually get deleted /// by making them pay rent. This creates an incentive to remove them early in order to save /// rent. const StorageSizeOffset: u32 = T::StorageSizeOffset::get(); /// Price of a byte of storage per one block interval. Should be greater than 0. const RentByteFee: BalanceOf = T::RentByteFee::get(); /// The amount of funds a contract should deposit in order to offset /// the cost of one byte. /// /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day, /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent. /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000, /// then it would pay 500 BU/day. const RentDepositOffset: BalanceOf = T::RentDepositOffset::get(); /// Reward that is received by the party whose touch has led /// to removal of a contract. const SurchargeReward: BalanceOf = T::SurchargeReward::get(); /// The maximum nesting level of a call/instantiate stack. A reasonable default /// value is 100. const MaxDepth: u32 = T::MaxDepth::get(); /// The maximum size of a storage value in bytes. A reasonable default is 16 KiB. const MaxValueSize: u32 = T::MaxValueSize::get(); fn deposit_event() = default; /// Updates the schedule for metering contracts. /// /// The schedule must have a greater version than the stored schedule. #[weight = T::WeightInfo::update_schedule()] pub fn update_schedule(origin, schedule: Schedule) -> DispatchResult { ensure_root(origin)?; if >::current_schedule().version >= schedule.version { Err(Error::::InvalidScheduleVersion)? } Self::deposit_event(RawEvent::ScheduleUpdated(schedule.version)); CurrentSchedule::put(schedule); Ok(()) } /// Stores the given binary Wasm code into the chain's storage and returns its `codehash`. /// You can instantiate contracts only with stored code. #[weight = T::WeightInfo::put_code(code.len() as u32 / 1024)] pub fn put_code( origin, code: Vec ) -> DispatchResult { ensure_signed(origin)?; let schedule = >::current_schedule(); ensure!(code.len() as u32 <= schedule.limits.code_size, Error::::CodeTooLarge); let result = wasm::save_code::(code, &schedule); if let Ok(code_hash) = result { Self::deposit_event(RawEvent::CodeStored(code_hash)); } result.map(|_| ()).map_err(Into::into) } /// Makes a call to an account, optionally transferring some balance. /// /// * If the account is a smart-contract account, the associated code will be /// executed and any value will be transferred. /// * If the account is a regular account, any value will be transferred. /// * If no account exists and the call value is not less than `existential_deposit`, /// a regular account will be created and any value will be transferred. #[weight = T::WeightInfo::call().saturating_add(*gas_limit)] pub fn call( origin, dest: ::Source, #[compact] value: BalanceOf, #[compact] gas_limit: Gas, data: Vec ) -> DispatchResultWithPostInfo { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; let mut gas_meter = GasMeter::new(gas_limit); let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| { ctx.call(dest, value, gas_meter, data) }); gas_meter.into_dispatch_result(result) } /// Instantiates a new contract from the `code_hash` generated by `put_code`, /// optionally transferring some balance. /// /// The supplied `salt` is used for contract address deriviation. See `fn contract_address`. /// /// Instantiation is executed as follows: /// /// - The destination address is computed based on the sender, code_hash and the salt. /// - The smart-contract account is created at the computed address. /// - The `ctor_code` is executed in the context of the newly-created account. Buffer returned /// after the execution is saved as the `code` of the account. That code will be invoked /// upon any call received by this account. /// - The contract is initialized. #[weight = T::WeightInfo::instantiate( data.len() as u32 / 1024, salt.len() as u32 / 1024, ).saturating_add(*gas_limit) ] pub fn instantiate( origin, #[compact] endowment: BalanceOf, #[compact] gas_limit: Gas, code_hash: CodeHash, data: Vec, salt: Vec, ) -> DispatchResultWithPostInfo { let origin = ensure_signed(origin)?; let mut gas_meter = GasMeter::new(gas_limit); let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| { ctx.instantiate(endowment, gas_meter, &code_hash, data, &salt) .map(|(_address, output)| output) }); gas_meter.into_dispatch_result(result) } /// Allows block producers to claim a small reward for evicting a contract. If a block producer /// fails to do so, a regular users will be allowed to claim the reward. /// /// If contract is not evicted as a result of this call, no actions are taken and /// the sender is not eligible for the reward. #[weight = T::WeightInfo::claim_surcharge()] fn claim_surcharge(origin, dest: T::AccountId, aux_sender: Option) { let origin = origin.into(); let (signed, rewarded) = match (origin, aux_sender) { (Ok(frame_system::RawOrigin::Signed(account)), None) => { (true, account) }, (Ok(frame_system::RawOrigin::None), Some(aux_sender)) => { (false, aux_sender) }, _ => Err(Error::::InvalidSurchargeClaim)?, }; // Add some advantage for block producers (who send unsigned extrinsics) by // adding a handicap: for signed extrinsics we use a slightly older block number // for the eviction check. This can be viewed as if we pushed regular users back in past. let handicap = if signed { T::SignedClaimHandicap::get() } else { Zero::zero() }; // If poking the contract has lead to eviction of the contract, give out the rewards. if Rent::::snitch_contract_should_be_evicted(&dest, handicap) { T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?; } } } } /// Public APIs provided by the contracts module. impl Module where T::AccountId: UncheckedFrom + AsRef<[u8]>, { /// Perform a call to a specified contract. /// /// This function is similar to `Self::call`, but doesn't perform any address lookups and better /// suitable for calling directly from Rust. /// /// It returns the exection result and the amount of used weight. pub fn bare_call( origin: T::AccountId, dest: T::AccountId, value: BalanceOf, gas_limit: Gas, input_data: Vec, ) -> ContractExecResult { let mut gas_meter = GasMeter::new(gas_limit); let exec_result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| { ctx.call(dest, value, gas_meter, input_data) }); let gas_consumed = gas_meter.gas_spent(); ContractExecResult { exec_result, gas_consumed, } } /// Query storage of a specified contract under a specified key. pub fn get_storage(address: T::AccountId, key: [u8; 32]) -> GetStorageResult { let contract_info = ContractInfoOf::::get(&address) .ok_or(ContractAccessError::DoesntExist)? .get_alive() .ok_or(ContractAccessError::IsTombstone)?; let maybe_value = Storage::::read(&contract_info.trie_id, &key); Ok(maybe_value) } pub fn rent_projection(address: T::AccountId) -> RentProjectionResult { Rent::::compute_projection(&address) } /// Put code for benchmarks which does not check or instrument the code. #[cfg(feature = "runtime-benchmarks")] pub fn put_code_raw(code: Vec) -> DispatchResult { let schedule = >::current_schedule(); let result = wasm::save_code_raw::(code, &schedule); result.map(|_| ()).map_err(Into::into) } /// Determine the address of a contract, /// /// This is the address generation function used by contract instantation. Its result /// is only dependend on its inputs. It can therefore be used to reliably predict the /// address of a contract. This is akin to the formular of eth's CRATE2 opcode. There /// is no CREATE equivalent because CREATE2 is strictly more powerful. /// /// Formula: `hash(deploying_address ++ code_hash ++ salt)` pub fn contract_address( deploying_address: &T::AccountId, code_hash: &CodeHash, salt: &[u8], ) -> T::AccountId { let buf: Vec<_> = deploying_address.as_ref().iter() .chain(code_hash.as_ref()) .chain(salt) .cloned() .collect(); UncheckedFrom::unchecked_from(T::Hashing::hash(&buf)) } } impl Module where T::AccountId: UncheckedFrom + AsRef<[u8]>, { fn execute_wasm( origin: T::AccountId, gas_meter: &mut GasMeter, func: impl FnOnce(&mut ExecutionContext, WasmLoader>, &mut GasMeter) -> ExecResult, ) -> ExecResult { let cfg = ConfigCache::preload(); let vm = WasmVm::new(&cfg.schedule); let loader = WasmLoader::new(&cfg.schedule); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); func(&mut ctx, gas_meter) } } decl_event! { pub enum Event where Balance = BalanceOf, ::AccountId, ::Hash { /// Contract deployed by address at the specified address. \[owner, contract\] Instantiated(AccountId, AccountId), /// Contract has been evicted and is now in tombstone state. /// \[contract, tombstone\] /// /// # Params /// /// - `contract`: `AccountId`: The account ID of the evicted contract. /// - `tombstone`: `bool`: True if the evicted contract left behind a tombstone. Evicted(AccountId, bool), /// Restoration for a contract has been successful. /// \[donor, dest, code_hash, rent_allowance\] /// /// # Params /// /// - `donor`: `AccountId`: Account ID of the restoring contract /// - `dest`: `AccountId`: Account ID of the restored contract /// - `code_hash`: `Hash`: Code hash of the restored contract /// - `rent_allowance: `Balance`: Rent allowance of the restored contract Restored(AccountId, AccountId, Hash, Balance), /// Code with the specified hash has been stored. /// \[code_hash\] CodeStored(Hash), /// Triggered when the current \[schedule\] is updated. ScheduleUpdated(u32), /// An event deposited upon execution of a contract from the account. /// \[account, data\] ContractExecution(AccountId, Vec), } } decl_storage! { trait Store for Module as Contracts where T::AccountId: UncheckedFrom + AsRef<[u8]> { /// Current cost schedule for contracts. CurrentSchedule get(fn current_schedule) config(): Schedule = Default::default(); /// A mapping from an original code hash to the original code, untouched by instrumentation. pub PristineCode: map hasher(identity) CodeHash => Option>; /// A mapping between an original code hash and instrumented wasm code, ready for execution. pub CodeStorage: map hasher(identity) CodeHash => Option; /// The subtrie counter. pub AccountCounter: u64 = 0; /// The code associated with a given account. /// /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. pub ContractInfoOf: map hasher(twox_64_concat) T::AccountId => Option>; } } /// In-memory cache of configuration values. /// /// We assume that these values can't be changed in the /// course of transaction execution. pub struct ConfigCache { pub schedule: Schedule, pub existential_deposit: BalanceOf, pub tombstone_deposit: BalanceOf, pub max_depth: u32, pub max_value_size: u32, } impl ConfigCache where T::AccountId: UncheckedFrom + AsRef<[u8]> { fn preload() -> ConfigCache { ConfigCache { schedule: >::current_schedule(), existential_deposit: T::Currency::minimum_balance(), tombstone_deposit: T::TombstoneDeposit::get(), max_depth: T::MaxDepth::get(), max_value_size: T::MaxValueSize::get(), } } /// Subsistence threshold is the extension of the minimum balance (aka existential deposit) by the /// tombstone deposit, required for leaving a tombstone. /// /// Rent or any contract initiated balance transfer mechanism cannot make the balance lower /// than the subsistence threshold in order to guarantee that a tombstone is created. /// /// The only way to completely kill a contract without a tombstone is calling `seal_terminate`. pub fn subsistence_threshold(&self) -> BalanceOf { self.existential_deposit.saturating_add(self.tombstone_deposit) } /// The same as `subsistence_threshold` but without the need for a preloaded instance. /// /// This is for cases where this value is needed in rent calculation rather than /// during contract execution. pub fn subsistence_threshold_uncached() -> BalanceOf { T::Currency::minimum_balance().saturating_add(T::TombstoneDeposit::get()) } }