Newer
Older
/// Termination of a contract is not allowed while the contract is already
/// on the call stack. Can be triggered by `seal_terminate`.
TerminatedWhileReentrant,
/// `seal_call` forwarded this contracts input. It therefore is no longer available.
InputForwarded,
/// The subject passed to `seal_random` exceeds the limit.
RandomSubjectTooLong,
/// The amount of topics passed to `seal_deposit_events` exceeds the limit.
TooManyTopics,
/// The chain does not provide a chain extension. Calling the chain extension results
/// in this error. Note that this usually shouldn't happen as deploying such contracts
/// is rejected.
NoChainExtension,
/// Failed to decode the XCM program.
XCMDecodeFailed,
/// A contract with the same AccountId already exists.
DuplicateContract,
/// A contract self destructed in its constructor.
///
/// This can be triggered by a call to `seal_terminate`.
TerminatedInConstructor,
/// A call tried to invoke a contract that is flagged as non-reentrant.
Sasha Gryaznov
committed
/// The only other cause is that a call from a contract into the runtime tried to call back
/// into `pallet-contracts`. This would make the whole pallet reentrant with regard to
/// contract code execution which is not supported.
ReentranceDenied,
/// Origin doesn't have enough balance to pay the required storage deposits.
StorageDepositNotEnoughFunds,
/// More storage was created than allowed by the storage deposit limit.
StorageDepositLimitExhausted,
/// Code removal was denied because the code is still in use by at least one contract.
CodeInUse,
/// The contract ran to completion but decided to revert its storage changes.
/// Please note that this error is only returned from extrinsics. When called directly
/// or via RPC an `Ok` will be returned. In this case the caller needs to inspect the flags
/// to determine whether a reversion has taken place.
ContractReverted,
/// The contract's code was found to be invalid during validation.
Alexander Theißen
committed
///
/// The most likely cause of this is that an API was used which is not supported by the
/// node. This happens if an older node is used with a new version of ink!. Try updating
Alexander Theißen
committed
/// your node to the newest available version.
///
/// A more detailed error can be found on the node console if debug messages are enabled
Alexander Theißen
committed
/// by supplying `-lruntime::contracts=debug`.
CodeRejected,
/// An indetermistic code was used in a context where this is not permitted.
Indeterministic,
/// A pending migration needs to complete before the extrinsic can be called.
MigrationInProgress,
/// Migrate dispatch call was attempted but no migration was performed.
NoMigrationPerformed,
/// The contract has reached its maximum number of delegate dependencies.
MaxDelegateDependenciesReached,
/// The dependency was not found in the contract's delegate dependencies.
DelegateDependencyNotFound,
/// The contract already depends on the given delegate dependency.
DelegateDependencyAlreadyExists,
/// Can not add a delegate dependency to the code hash of the contract itself.
CannotAddSelfAsDelegateDependency,
/// A reason for the pallet contracts placing a hold on funds.
#[pallet::composite_enum]
pub enum HoldReason {
/// The Pallet has reserved it for storing code on-chain.
CodeUploadDepositReserve,
/// The Pallet has reserved it for storage deposit.
StorageDepositReserve,
/// A mapping from a contract's code hash to its code.
pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, CodeVec<T>>;
/// A mapping from a contract's code hash to its code info.
pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, CodeHash<T>, CodeInfo<T>>;
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
/// This is a **monotonic** counter incremented on contract instantiation.
///
/// This is used in order to generate unique trie ids for contracts.
/// The trie id of a new contract is calculated from hash(account_id, nonce).
/// The nonce is required because otherwise the following sequence would lead to
/// a possible collision of storage:
///
/// 1. Create a new contract.
/// 2. Terminate the contract.
/// 3. Immediately recreate the contract with the same account_id.
///
/// This is bad because the contents of a trie are deleted lazily and there might be
/// storage of the old instantiation still in it when the new contract is created. Please
/// note that we can't replace the counter by the block number because the sequence above
/// can happen in the same block. We also can't keep the account counter in memory only
/// because storage is the only way to communicate across different extrinsics in the
/// same block.
///
/// # Note
///
/// Do not use it to determine the number of contracts. It won't be decremented if
/// a contract is destroyed.
pub(crate) type Nonce<T: Config> = StorageValue<_, u64, ValueQuery>;
/// The code associated with a given account.
///
/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
#[pallet::storage]
pub(crate) type ContractInfoOf<T: Config> =
StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;
/// Evicted contracts that await child trie deletion.
///
/// Child trie deletion is a heavy operation depending on the amount of storage items
PG Herveou
committed
/// stored in said trie. Therefore this operation is performed lazily in `on_idle`.
#[pallet::storage]
pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
/// A pair of monotonic counters used to track the latest contract marked for deletion
/// and the latest deleted contract in queue.
PG Herveou
committed
pub(crate) type DeletionQueueCounter<T: Config> =
StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
/// A migration can span across multiple blocks. This storage defines a cursor to track the
/// progress of the migration, enabling us to resume from the last completed position.
#[pallet::storage]
pub(crate) type MigrationInProgress<T: Config> =
StorageValue<_, migration::Cursor, OptionQuery>;
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
/// The type of origins supported by the contracts pallet.
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)]
pub enum Origin<T: Config> {
Root,
Signed(T::AccountId),
}
impl<T: Config> Origin<T> {
/// Creates a new Signed Caller from an AccountId.
pub fn from_account_id(account_id: T::AccountId) -> Self {
Origin::Signed(account_id)
}
/// Creates a new Origin from a `RuntimeOrigin`.
pub fn from_runtime_origin(o: OriginFor<T>) -> Result<Self, DispatchError> {
match o.into() {
Ok(RawOrigin::Root) => Ok(Self::Root),
Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)),
_ => Err(BadOrigin.into()),
}
}
/// Returns the AccountId of a Signed Origin or an error if the origin is Root.
pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> {
match self {
Origin::Signed(id) => Ok(id),
Origin::Root => Err(DispatchError::RootNotAllowed),
}
}
}
Sasha Gryaznov
committed
/// Context of a contract invocation.
struct CommonInput<'a, T: Config> {
Sasha Gryaznov
committed
value: BalanceOf<T>,
data: Vec<u8>,
gas_limit: Weight,
storage_deposit_limit: Option<BalanceOf<T>>,
debug_message: Option<&'a mut DebugBufferVec<T>>,
}
Sasha Gryaznov
committed
/// Input specific to a call into contract.
struct CallInput<T: Config> {
dest: T::AccountId,
determinism: Determinism,
}
/// Reference to an existing code hash or a new wasm module.
enum WasmCode<T: Config> {
Wasm(WasmBlob<T>),
CodeHash(CodeHash<T>),
}
Sasha Gryaznov
committed
/// Input specific to a contract instantiation invocation.
struct InstantiateInput<T: Config> {
Sasha Gryaznov
committed
salt: Vec<u8>,
}
/// Determines whether events should be collected during execution.
#[derive(
Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo,
)]
pub enum CollectEvents {
/// Collect events.
///
/// # Note
///
/// Events should only be collected when called off-chain, as this would otherwise
/// collect all the Events emitted in the block so far and put them into the PoV.
///
/// **Never** use this mode for on-chain execution.
UnsafeCollect,
/// Skip event collection.
Skip,
}
/// Determines whether debug messages will be collected.
#[derive(
Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo,
)]
pub enum DebugInfo {
/// Collect debug messages.
/// # Note
///
/// This should only ever be set to `UnsafeDebug` when executing as an RPC because
/// it adds allocations and could be abused to drive the runtime into an OOM panic.
UnsafeDebug,
/// Skip collection of debug messages.
Skip,
}
/// Return type of private helper functions.
struct InternalOutput<T: Config, O> {
/// The gas meter that was used to execute the call.
gas_meter: GasMeter<T>,
/// The storage deposit used by the call.
storage_deposit: StorageDeposit<BalanceOf<T>>,
/// The result of the call.
result: Result<O, ExecError>,
}
/// Helper trait to wrap contract execution entry points into a single function
Sasha Gryaznov
committed
/// [`Invokable::run_guarded`].
trait Invokable<T: Config>: Sized {
Sasha Gryaznov
committed
/// What is returned as a result of a successful invocation.
type Output;
/// Single entry point to contract execution.
/// Downstream execution flow is branched by implementations of [`Invokable`] trait:
///
/// - [`InstantiateInput::run`] runs contract instantiation,
/// - [`CallInput::run`] runs contract call.
///
/// We enforce a re-entrancy guard here by initializing and checking a boolean flag through a
/// global reference.
fn run_guarded(self, common: CommonInput<T>) -> InternalOutput<T, Self::Output> {
Sasha Gryaznov
committed
// Set up a global reference to the boolean flag used for the re-entrancy guard.
environmental!(executing_contract: bool);
let gas_limit = common.gas_limit;
// Check whether the origin is allowed here. The logic of the access rules
// is in the `ensure_origin`, this could vary for different implementations of this
// trait. For example, some actions might not allow Root origin as they could require an
// AccountId associated with the origin.
if let Err(e) = self.ensure_origin(common.origin.clone()) {
return InternalOutput {
gas_meter: GasMeter::new(gas_limit),
storage_deposit: Default::default(),
result: Err(ExecError { error: e.into(), origin: ErrorOrigin::Caller }),
}
}
Sasha Gryaznov
committed
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
executing_contract::using_once(&mut false, || {
executing_contract::with(|f| {
// Fail if already entered contract execution
if *f {
return Err(())
}
// We are entering contract execution
*f = true;
Ok(())
})
.expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
.map_or_else(
|_| InternalOutput {
gas_meter: GasMeter::new(gas_limit),
storage_deposit: Default::default(),
result: Err(ExecError {
error: <Error<T>>::ReentranceDenied.into(),
origin: ErrorOrigin::Caller,
}),
},
// Enter contract call.
|_| self.run(common, GasMeter::new(gas_limit)),
)
})
}
/// Method that does the actual call to a contract. It can be either a call to a deployed
/// contract or a instantiation of a new one.
///
/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
fn run(self, common: CommonInput<T>, gas_meter: GasMeter<T>)
-> InternalOutput<T, Self::Output>;
/// This method ensures that the given `origin` is allowed to invoke the current `Invokable`.
///
/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
fn ensure_origin(&self, origin: Origin<T>) -> Result<(), DispatchError>;
Sasha Gryaznov
committed
}
impl<T: Config> Invokable<T> for CallInput<T> {
type Output = ExecReturnValue;
fn run(
Sasha Gryaznov
committed
common: CommonInput<T>,
mut gas_meter: GasMeter<T>,
) -> InternalOutput<T, Self::Output> {
let CallInput { dest, determinism } = self;
let CommonInput { origin, value, data, debug_message, .. } = common;
Sasha Gryaznov
committed
let mut storage_meter =
match StorageMeter::new(&origin, common.storage_deposit_limit, common.value) {
Sasha Gryaznov
committed
Ok(meter) => meter,
Err(err) =>
return InternalOutput {
result: Err(err.into()),
gas_meter,
storage_deposit: Default::default(),
},
};
let schedule = T::Schedule::get();
let result = ExecStack::<T, WasmBlob<T>>::run_call(
Sasha Gryaznov
committed
origin.clone(),
dest.clone(),
&mut gas_meter,
&mut storage_meter,
&schedule,
value,
data.clone(),
debug_message,
Sasha Gryaznov
committed
);
match storage_meter.try_into_deposit(&origin) {
Ok(storage_deposit) => InternalOutput { gas_meter, storage_deposit, result },
Err(err) => InternalOutput {
gas_meter,
storage_deposit: Default::default(),
result: Err(err.into()),
},
}
Sasha Gryaznov
committed
}
fn ensure_origin(&self, _origin: Origin<T>) -> Result<(), DispatchError> {
Ok(())
}
Sasha Gryaznov
committed
}
impl<T: Config> Invokable<T> for InstantiateInput<T> {
type Output = (AccountIdOf<T>, ExecReturnValue);
fn run(
self,
common: CommonInput<T>,
Sasha Gryaznov
committed
mut gas_meter: GasMeter<T>,
) -> InternalOutput<T, Self::Output> {
let mut storage_deposit = Default::default();
let try_exec = || {
let schedule = T::Schedule::get();
let InstantiateInput { salt, .. } = self;
let CommonInput { origin: contract_origin, .. } = common;
let origin = contract_origin.account_id()?;
let executable = match self.code {
WasmCode::Wasm(module) => module,
WasmCode::CodeHash(code_hash) => WasmBlob::from_storage(code_hash, &mut gas_meter)?,
Sasha Gryaznov
committed
};
let contract_origin = Origin::from_account_id(origin.clone());
let mut storage_meter =
StorageMeter::new(&contract_origin, common.storage_deposit_limit, common.value)?;
let CommonInput { value, data, debug_message, .. } = common;
let result = ExecStack::<T, WasmBlob<T>>::run_instantiate(
Sasha Gryaznov
committed
origin.clone(),
executable,
&mut gas_meter,
&mut storage_meter,
&schedule,
value,
data.clone(),
&salt,
debug_message,
);
storage_deposit = storage_meter.try_into_deposit(&contract_origin)?;
Sasha Gryaznov
committed
result
};
InternalOutput { result: try_exec(), gas_meter, storage_deposit }
}
fn ensure_origin(&self, origin: Origin<T>) -> Result<(), DispatchError> {
match origin {
Origin::Signed(_) => Ok(()),
Origin::Root => Err(DispatchError::RootNotAllowed),
}
}
Sasha Gryaznov
committed
}
macro_rules! ensure_no_migration_in_progress {
() => {
if Migration::<T>::in_progress() {
return ContractResult {
gas_consumed: Zero::zero(),
gas_required: Zero::zero(),
storage_deposit: Default::default(),
debug_message: Vec::new(),
result: Err(Error::<T>::MigrationInProgress.into()),
events: None,
}
}
};
}
impl<T: Config> Pallet<T> {
/// 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.
/// # Note
///
/// If `debug` is set to `DebugInfo::UnsafeDebug` it returns additional human readable debugging
/// information.
/// If `collect_events` is set to `CollectEvents::UnsafeCollect` it collects all the Events
/// emitted in the block so far and the ones emitted during the execution of this contract.
pub fn bare_call(
origin: T::AccountId,
dest: T::AccountId,
value: BalanceOf<T>,
storage_deposit_limit: Option<BalanceOf<T>>,
data: Vec<u8>,
debug: DebugInfo,
collect_events: CollectEvents,
determinism: Determinism,
) -> ContractExecResult<BalanceOf<T>, EventRecordOf<T>> {
ensure_no_migration_in_progress!();
let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) {
Some(DebugBufferVec::<T>::default())
} else {
None
};
let origin = Origin::from_account_id(origin);
Sasha Gryaznov
committed
let common = CommonInput {
Sasha Gryaznov
committed
data,
gas_limit,
storage_deposit_limit,
Sasha Gryaznov
committed
debug_message: debug_message.as_mut(),
};
let output = CallInput::<T> { dest, determinism }.run_guarded(common);
let events = if matches!(collect_events, CollectEvents::UnsafeCollect) {
Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
} else {
None
};
Alexander Theißen
committed
ContractExecResult {
result: output.result.map_err(|r| r.error),
gas_consumed: output.gas_meter.gas_consumed(),
gas_required: output.gas_meter.gas_required(),
storage_deposit: output.storage_deposit,
debug_message: debug_message.unwrap_or_default().to_vec(),
}
}
/// Instantiate a new contract.
///
/// This function is similar to [`Self::instantiate`], but doesn't perform any address lookups
/// and better suitable for calling directly from Rust.
///
/// It returns the execution result, account id and the amount of used weight.
///
/// # Note
///
/// If `debug` is set to `DebugInfo::UnsafeDebug` it returns additional human readable debugging
/// information.
///
/// If `collect_events` is set to `CollectEvents::UnsafeCollect` it collects all the Events
/// emitted in the block so far.
pub fn bare_instantiate(
origin: T::AccountId,
gas_limit: Weight,
mut storage_deposit_limit: Option<BalanceOf<T>>,
code: Code<CodeHash<T>>,
data: Vec<u8>,
salt: Vec<u8>,
debug: DebugInfo,
collect_events: CollectEvents,
) -> ContractInstantiateResult<T::AccountId, BalanceOf<T>, EventRecordOf<T>> {
ensure_no_migration_in_progress!();
let mut debug_message = if debug == DebugInfo::UnsafeDebug {
Some(DebugBufferVec::<T>::default())
} else {
None
};
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
// collect events if CollectEvents is UnsafeCollect
let events = || {
if collect_events == CollectEvents::UnsafeCollect {
Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
} else {
None
}
};
let (code, upload_deposit): (WasmCode<T>, BalanceOf<T>) = match code {
Code::Upload(code) => {
let result = Self::try_upload_code(
origin.clone(),
code,
storage_deposit_limit.map(Into::into),
Determinism::Enforced,
debug_message.as_mut(),
);
let (module, deposit) = match result {
Ok(result) => result,
Err(error) =>
return ContractResult {
gas_consumed: Zero::zero(),
gas_required: Zero::zero(),
storage_deposit: Default::default(),
debug_message: debug_message.unwrap_or(Default::default()).into(),
result: Err(error),
events: events(),
},
};
storage_deposit_limit =
storage_deposit_limit.map(|l| l.saturating_sub(deposit.into()));
(WasmCode::Wasm(module), deposit)
},
Code::Existing(hash) => (WasmCode::CodeHash(hash), Default::default()),
};
Sasha Gryaznov
committed
let common = CommonInput {
origin: Origin::from_account_id(origin),
Sasha Gryaznov
committed
data,
gas_limit,
Sasha Gryaznov
committed
debug_message: debug_message.as_mut(),
};
Sasha Gryaznov
committed
let output = InstantiateInput::<T> { code, salt }.run_guarded(common);
ContractInstantiateResult {
result: output
.result
.map(|(account_id, result)| InstantiateReturnValue { result, account_id })
.map_err(|e| e.error),
gas_consumed: output.gas_meter.gas_consumed(),
gas_required: output.gas_meter.gas_required(),
storage_deposit: output
.storage_deposit
.saturating_add(&StorageDeposit::Charge(upload_deposit)),
debug_message: debug_message.unwrap_or_default().to_vec(),
Alexander Theißen
committed
}
/// Upload new code without instantiating a contract from it.
///
/// This function is similar to [`Self::upload_code`], but doesn't perform any address lookups
/// and better suitable for calling directly from Rust.
pub fn bare_upload_code(
origin: T::AccountId,
code: Vec<u8>,
storage_deposit_limit: Option<BalanceOf<T>>,
determinism: Determinism,
) -> CodeUploadResult<CodeHash<T>, BalanceOf<T>> {
Migration::<T>::ensure_migrated()?;
let (module, deposit) =
Self::try_upload_code(origin, code, storage_deposit_limit, determinism, None)?;
Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
}
/// Uploads new code and returns the Wasm blob and deposit amount collected.
fn try_upload_code(
origin: T::AccountId,
code: Vec<u8>,
storage_deposit_limit: Option<BalanceOf<T>>,
determinism: Determinism,
mut debug_message: Option<&mut DebugBufferVec<T>>,
) -> Result<(WasmBlob<T>, BalanceOf<T>), DispatchError> {
let schedule = T::Schedule::get();
let mut module =
WasmBlob::from_code(code, &schedule, origin, determinism).map_err(|(err, msg)| {
debug_message.as_mut().map(|d| d.try_extend(msg.bytes()));
err
})?;
let deposit = module.store_code()?;
if let Some(storage_deposit_limit) = storage_deposit_limit {
ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
}
Ok((module, deposit))
/// Query storage of a specified contract under a specified key.
pub fn get_storage(address: T::AccountId, key: Vec<u8>) -> GetStorageResult {
if Migration::<T>::in_progress() {
return Err(ContractAccessError::MigrationInProgress)
}
let contract_info =
ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
Alexander Theißen
committed
let maybe_value = contract_info.read(
&Key::<T>::try_from_var(key)
.map_err(|_| ContractAccessError::KeyDecodingFailed)?
.into(),
);
/// Determine the address of a contract.
///
/// This is the address generation function used by contract instantiation. See
/// [`DefaultAddressGenerator`] for the default implementation.
pub fn contract_address(
deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
input_data: &[u8],
salt: &[u8],
Alexander Theißen
committed
T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt)
/// Returns the code hash of the contract specified by `account` ID.
pub fn code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>> {
Alexander Theißen
committed
ContractInfo::<T>::load_code_hash(account)
/// Store code for benchmarks which does not validate the code.
#[cfg(feature = "runtime-benchmarks")]
fn store_code_raw(
code: Vec<u8>,
owner: T::AccountId,
) -> frame_support::dispatch::DispatchResult {
let schedule = T::Schedule::get();
WasmBlob::<T>::from_code_unchecked(code, &schedule, owner)?.store_code()?;
Ok(())
}
Alexander Theißen
committed
/// Deposit a pallet contracts event. Handles the conversion to the overarching event type.
fn deposit_event(topics: Vec<T::Hash>, event: Event<T>) {
<frame_system::Pallet<T>>::deposit_event_indexed(
&topics,
<T as Config>::RuntimeEvent::from(event).into(),
Alexander Theißen
committed
/// Return the existential deposit of [`Config::Currency`].
fn min_balance() -> BalanceOf<T> {
<T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
}
/// Convert gas_limit from 1D Weight to a 2D Weight.
/// Used by backwards compatible extrinsics. We cannot just set the proof_size weight limit to
/// zero or an old `Call` will just fail with OutOfGas.
fn compat_weight_limit(gas_limit: OldWeight) -> Weight {
Weight::from_parts(gas_limit, u64::from(T::MaxCodeLen::get()) * 2)
Jim Posen
committed
}
sp_api::decl_runtime_apis! {
/// The API used to dry-run contract interactions.
pub trait ContractsApi<AccountId, Balance, BlockNumber, Hash, EventRecord> where
AccountId: Codec,
Balance: Codec,
BlockNumber: Codec,
Hash: Codec,
{
/// Perform a call from a specified account to a given contract.
///
/// See [`crate::Pallet::bare_call`].
fn call(
origin: AccountId,
dest: AccountId,
value: Balance,
gas_limit: Option<Weight>,
storage_deposit_limit: Option<Balance>,
input_data: Vec<u8>,
) -> ContractExecResult<Balance, EventRecord>;
/// Instantiate a new contract.
///
/// See `[crate::Pallet::bare_instantiate]`.
fn instantiate(
origin: AccountId,
value: Balance,
gas_limit: Option<Weight>,
storage_deposit_limit: Option<Balance>,
code: Code<Hash>,
data: Vec<u8>,
salt: Vec<u8>,
) -> ContractInstantiateResult<AccountId, Balance, EventRecord>;
/// Upload new code without instantiating a contract from it.
///
/// See [`crate::Pallet::bare_upload_code`].
fn upload_code(
origin: AccountId,
code: Vec<u8>,
storage_deposit_limit: Option<Balance>,
determinism: Determinism,
) -> CodeUploadResult<Hash, Balance>;
/// Query a given storage key in a given contract.
///
/// Returns `Ok(Some(Vec<u8>))` if the storage value exists under the given key in the
/// specified account and `Ok(None)` if it doesn't. If the account specified by the address
/// doesn't exist, or doesn't have a contract then `Err` is returned.
fn get_storage(
address: AccountId,
key: Vec<u8>,
) -> GetStorageResult;
}
}