runtime.rs 92.9 KiB
Newer Older
	/// Stores the *free* balance of the current account into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	///
	fn balance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Balance)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.balance().encode(),
			false,
			already_charged,
	/// Stores the value transferred along with this call/instantiate into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a `u32` value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	///
		out_ptr: u32,
		out_len_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::ValueTransferred)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.value_transferred().encode(),
			false,
			already_charged,
	/// Stores a random number for the current block and the given subject into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	///
		subject_ptr: u32,
		subject_len: u32,
		out_ptr: u32,
		out_len_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Random)?;
		if subject_len > ctx.ext.schedule().limits.subject_len {
			return Err(Error::<E::T>::RandomSubjectTooLong.into())
		let subject_buf = ctx.read_sandbox_memory(memory, subject_ptr, subject_len)?;
		Ok(ctx.write_sandbox_output(
			out_ptr,
			out_len_ptr,
			&ctx.ext.random(&subject_buf).0.encode(),
			false,
			already_charged,
	/// Stores a random number for the current block and the given subject into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	///
	/// The data is encoded as (T::Hash, T::BlockNumber).
	///
	/// # Changes from v0
	///
	/// In addition to the seed it returns the block number since which it was determinable
	/// by chain observers.
	///
	/// # Note
	///
	/// The returned seed should only be used to distinguish commitments made before
	/// the returned block number. If the block number is too early (i.e. commitments were
	/// made afterwards), then ensure no further commitments may be made and repeatedly
	/// call this on later blocks until the block number returned is later than the latest
	/// commitment.
	#[version(1)]
		subject_ptr: u32,
		subject_len: u32,
		out_ptr: u32,
		out_len_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Random)?;
		if subject_len > ctx.ext.schedule().limits.subject_len {
			return Err(Error::<E::T>::RandomSubjectTooLong.into())
		let subject_buf = ctx.read_sandbox_memory(memory, subject_ptr, subject_len)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.random(&subject_buf).encode(),
			false,
			already_charged,
	/// Load the latest block timestamp into the supplied buffer
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	fn now(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Now)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.now().encode(),
			false,
			already_charged,
	/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
	///
	fn minimum_balance(
		ctx: _,
		memory: _,
		out_ptr: u32,
		out_len_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::MinimumBalance)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.minimum_balance().encode(),
			false,
			already_charged,
	/// Stores the tombstone deposit into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	///
	/// There is no longer a tombstone deposit. This function always returns `0`.
		out_ptr: u32,
		out_len_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Balance)?;
		let deposit = <BalanceOf<E::T>>::zero().encode();
		Ok(ctx.write_sandbox_output(
			memory,
			out_ptr,
			out_len_ptr,
			&deposit,
			false,
			already_charged,
		)?)
	/// Was used to restore the given destination contract sacrificing the caller.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatibility
		_dest_ptr: u32,
		_code_hash_ptr: u32,
		_rent_allowance_ptr: u32,
		_rent_allowance_len: u32,
		_delta_ptr: u32,
		_delta_count: u32,
	) -> Result<(), TrapReason> {
	/// Was used to restore the given destination contract sacrificing the caller.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatibility
		_dest_ptr: u32,
		_code_hash_ptr: u32,
		_rent_allowance_ptr: u32,
		_delta_ptr: u32,
		_delta_count: u32,
	) -> Result<(), TrapReason> {
	/// Was used to set rent allowance of the contract.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatibility.
		_value_ptr: u32,
		_value_len: u32,
	) -> Result<(), TrapReason> {
	/// Was used to set rent allowance of the contract.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatibility.
	fn set_rent_allowance(ctx: _, _memory: _, _value_ptr: u32) -> Result<(), TrapReason> {
	/// Was used to store the rent allowance into the supplied buffer.
	///
	/// # Note
	///
	/// The state rent functionality was removed. This is stub only exists for
	/// backwards compatibility.
	fn rent_allowance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::Balance)?;
		let rent_allowance = <BalanceOf<E::T>>::max_value().encode();
			out_ptr,
			out_len_ptr,
			&rent_allowance,
			false,
			already_charged,
	/// Deposit a contract event with the data buffer and optional list of topics. There is a limit
	/// on the maximum number of topics specified by `event_topics`.
	///
	/// - `topics_ptr`: a pointer to the buffer of topics encoded as `Vec<T::Hash>`. The value of
	///   this is ignored if `topics_len` is set to `0`. The topics list can't contain duplicates.
	/// - `topics_len`:  the length of the topics buffer. Pass 0 if you want to pass an empty
	///   vector.
	/// - `data_ptr`: a pointer to a raw data buffer which will saved along the event.
	/// - `data_len`:  the length of the data buffer.
	#[prefixed_alias]
	fn deposit_event(
		ctx: _,
		memory: _,
		topics_ptr: u32,
		topics_len: u32,
		data_ptr: u32,
		data_len: u32,
	) -> Result<(), TrapReason> {
		let num_topic = topics_len
			.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
			.ok_or("Zero sized topics are not allowed")?;
		ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
		if data_len > ctx.ext.max_value_size() {
			return Err(Error::<E::T>::ValueTooLarge.into())
		}

		let topics: Vec<TopicOf<<E as Ext>::T>> = match topics_len {
			0 => Vec::new(),
			_ => ctx.read_sandbox_memory_as_unbounded(memory, topics_ptr, topics_len)?,
		};

		// If there are more than `event_topics`, then trap.
		if topics.len() > ctx.ext.schedule().limits.event_topics as usize {
			return Err(Error::<E::T>::TooManyTopics.into())
		}

		let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?;

		ctx.ext.deposit_event(topics, event_data);

		Ok(())
	}

	/// Stores the current block number of the current contract into the supplied buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// `out_len_ptr` must point to a u32 value that describes the available space at
	/// `out_ptr`. This call overwrites it with the size of the value. If the available
	/// space at `out_ptr` is less than the size of the value a trap is triggered.
	fn block_number(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::BlockNumber)?;
			out_ptr,
			out_len_ptr,
			&ctx.ext.block_number().encode(),
			false,
			already_charged,
	/// Computes the SHA2 256-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 32 bytes (256 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, sha2_256, input_ptr, input_len, output_ptr,
		)?)
	/// Computes the KECCAK 256-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 32 bytes (256 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, keccak_256, input_ptr, input_len, output_ptr,
		)?)
	/// Computes the BLAKE2 256-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 32 bytes (256 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, blake2_256, input_ptr, input_len, output_ptr,
		)?)
	/// Computes the BLAKE2 128-bit hash on the given input buffer.
	///
	/// Returns the result directly into the given output buffer.
	///
	/// # Note
	///
	/// - The `input` and `output` buffer may overlap.
	/// - The output buffer is expected to hold at least 16 bytes (128 bits).
	/// - It is the callers responsibility to provide an output buffer that is large enough to hold
	///   the expected amount of bytes returned by the chosen hash function.
	///
	/// # Parameters
	///
	/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `input_len`: the length of the input data in bytes.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
	) -> Result<(), TrapReason> {
		ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
		Ok(ctx.compute_hash_on_intermediate_buffer(
			memory, blake2_128, input_ptr, input_len, output_ptr,
		)?)
	/// Call into the chain extension provided by the chain if any.
	///
	/// Handling of the input values is up to the specific chain extension and so is the
	/// return value. The extension can decide to use the inputs as primitive inputs or as
	/// in/out arguments by interpreting them as pointers. Any caller of this function
	/// must therefore coordinate with the chain that it targets.
	///
	/// # Note
	///
	/// If no chain extension exists the contract will trap with the `NoChainExtension`
	/// module error.
		input_ptr: u32,
		input_len: u32,
		output_ptr: u32,
		output_len_ptr: u32,
	) -> Result<u32, TrapReason> {
		use crate::chain_extension::{ChainExtension, Environment, RetVal};
		if !<E::T as Config>::ChainExtension::enabled() {
			return Err(Error::<E::T>::NoChainExtension.into())
		let mut chain_extension = ctx.chain_extension.take().expect(
			"Constructor initializes with `Some`. This is the only place where it is set to `None`.\
			It is always reset to `Some` afterwards. qed"
		);
		let env =
			Environment::new(ctx, memory, id, input_ptr, input_len, output_ptr, output_len_ptr);
		let ret = match chain_extension.call(env)? {
			RetVal::Diverging { flags, data } =>
				Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })),
		};
		ctx.chain_extension = Some(chain_extension);
		ret
	/// Emit a custom debug message.
	///
	/// No newlines are added to the supplied message.
	/// Specifying invalid UTF-8 just drops the message with no trap.
	///
	/// This is a no-op if debug message recording is disabled which is always the case
	/// when the code is executing on-chain. The message is interpreted as UTF-8 and
	/// appended to the debug buffer which is then supplied to the calling RPC client.
	///
	/// # Note
	///
	/// Even though no action is taken when debug message recording is disabled there is still
	/// a non trivial overhead (and weight cost) associated with calling this function. Contract
	/// languages should remove calls to this function (either at runtime or compile time) when
	/// not being executed as an RPC. For example, they could allow users to disable logging
	/// through compile time flags (cargo features) for on-chain deployment. Additionally, the
	/// return value of this function can be cached in order to prevent further calls at runtime.
		str_ptr: u32,
		str_len: u32,
	) -> Result<ReturnCode, TrapReason> {
		let str_len = str_len.min(DebugBufferVec::<E::T>::bound() as u32);
		ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?;
		if ctx.ext.append_debug_buffer("") {
			let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?;
			if let Some(msg) = core::str::from_utf8(&data).ok() {
				ctx.ext.append_debug_buffer(msg);
			}
	/// Call some dispatchable of the runtime.
	///
	/// This function decodes the passed in data as the overarching `Call` type of the
	/// runtime and dispatches it. The weight as specified in the runtime is charged
	/// from the gas meter. Any weight refunds made by the dispatchable are considered.
	///
	/// The filter specified by `Config::CallFilter` is attached to the origin of
	/// the dispatched call.
	///
	/// # Parameters
	///
	/// - `call_ptr`: the pointer into the linear memory where the input data is placed.
	/// - `call_len`: the length of the input data in bytes.
	/// Returns `ReturnCode::Success` when the dispatchable was successfully executed and
	/// returned `Ok`. When the dispatchable was exeuted but returned an error
	/// `ReturnCode::CallRuntimeFailed` is returned. The full error is not
	/// provided because it is not guaranteed to be stable.
	///
	/// # Comparison with `ChainExtension`
	///
	/// Just as a chain extension this API allows the runtime to extend the functionality
	/// of contracts. While making use of this function is generally easier it cannot be
	/// used in all cases. Consider writing a chain extension if you need to do perform
	/// one of the following tasks:
	///
	/// - Return data.
	/// - Provide functionality **exclusively** to contracts.
	/// - Provide custom weights.
	/// - Avoid the need to keep the `Call` data structure stable.
		call_ptr: u32,
		call_len: u32,
	) -> Result<ReturnCode, TrapReason> {
		use frame_support::dispatch::{extract_actual_weight, GetDispatchInfo};
		ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
		let call: <E::T as Config>::RuntimeCall =
			ctx.read_sandbox_memory_as_unbounded(memory, call_ptr, call_len)?;
		let dispatch_info = call.get_dispatch_info();
		let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?;
		let result = ctx.ext.call_runtime(call);
		let actual_weight = extract_actual_weight(&result, &dispatch_info);
		ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight));
		match result {
			Ok(_) => Ok(ReturnCode::Success),
			Err(e) => {
				if ctx.ext.append_debug_buffer("") {
					ctx.ext.append_debug_buffer("seal0::call_runtime failed with: ");
					ctx.ext.append_debug_buffer(e.into());
				};
				Ok(ReturnCode::CallRuntimeFailed)
			},
	/// Recovers the ECDSA public key from the given message hash and signature.
	///
	/// Writes the public key into the given output buffer.
	/// Assumes the secp256k1 curve.
	///
	/// # Parameters
	///
	/// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should
	///   be decodable as a 65 bytes. Traps otherwise.
	/// - `message_hash_ptr`: the pointer into the linear memory where the message hash is placed.
	///   Should be decodable as a 32 bytes. Traps otherwise.
	/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
	///   buffer should be 33 bytes. The function will write the result directly into this buffer.
	///
	/// # Errors
	///
		signature_ptr: u32,
		message_hash_ptr: u32,
		output_ptr: u32,
	) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?;

		let mut signature: [u8; 65] = [0; 65];
		ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?;
		let mut message_hash: [u8; 32] = [0; 32];
		ctx.read_sandbox_memory_into_buf(memory, message_hash_ptr, &mut message_hash)?;

		let result = ctx.ext.ecdsa_recover(&signature, &message_hash);

		match result {
			Ok(pub_key) => {
				// Write the recovered compressed ecdsa public key back into the sandboxed output
				// buffer.
				ctx.write_sandbox_memory(memory, output_ptr, pub_key.as_ref())?;

				Ok(ReturnCode::Success)
			},
			Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
		}
	/// Verify a sr25519 signature
	///
	/// # Parameters
	///
	/// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should
	///   be a value of 64 bytes.
	/// - `pub_key_ptr`: the pointer into the linear memory where the public key is placed. Should
	///   be a value of 32 bytes.
	/// - `message_len`: the length of the message payload.
	/// - `message_ptr`: the pointer into the linear memory where the message is placed.
	///
	/// # Errors
	///
	/// - `ReturnCode::Sr25519VerifyFailed
	#[unstable]
	fn sr25519_verify(
		ctx: _,
		memory: _,
		signature_ptr: u32,
		pub_key_ptr: u32,
		message_len: u32,
		message_ptr: u32,
	) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;

		let mut signature: [u8; 64] = [0; 64];
		ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?;

		let mut pub_key: [u8; 32] = [0; 32];
		ctx.read_sandbox_memory_into_buf(memory, pub_key_ptr, &mut pub_key)?;

		let message: Vec<u8> = ctx.read_sandbox_memory(memory, message_ptr, message_len)?;

		if ctx.ext.sr25519_verify(&signature, &message, &pub_key) {
			Ok(ReturnCode::Success)
		} else {
			Ok(ReturnCode::Sr25519VerifyFailed)
		}
	}

	/// Replace the contract code at the specified address with new code.
	///
	/// # Note
	///
	/// There are a couple of important considerations which must be taken into account when
	/// using this API:
	///
	/// 1. The storage at the code address will remain untouched. This means that contract
	/// developers must ensure that the storage layout of the new code is compatible with that of
	/// the old code.
	///
	/// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another
	/// way, when using this API you lose the guarantee that an address always identifies a specific
	/// code hash.
	/// 3. If a contract calls into itself after changing its code the new call would use
	/// the new code. However, if the original caller panics after returning from the sub call it
	/// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next
	/// caller would use the old code.
	///
	/// # Parameters
	///
	/// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash.
	///
	/// # Errors
	///
	fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::SetCodeHash)?;
		let code_hash: CodeHash<<E as Ext>::T> =
			ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
		match ctx.ext.set_code_hash(code_hash) {
				let code = Runtime::<E>::err_into_return_code(err)?;
				Ok(code)
			},
			Ok(()) => Ok(ReturnCode::Success),
	/// Calculates Ethereum address from the ECDSA compressed public key and stores
	/// it into the supplied buffer.
	///
	/// # Parameters
	///
	/// - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes
	///   value. Traps otherwise.
	/// - `out_ptr`: the pointer into the linear memory where the output data is placed. The
	///   function will write the result directly into this buffer.
	///
	/// The value is stored to linear memory at the address pointed to by `out_ptr`.
	/// If the available space at `out_ptr` is less than the size of the value a trap is triggered.
	///
	/// # Errors
	///
		key_ptr: u32,
		out_ptr: u32,
	) -> Result<ReturnCode, TrapReason> {
		ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
		let mut compressed_key: [u8; 33] = [0; 33];
		ctx.read_sandbox_memory_into_buf(memory, key_ptr, &mut compressed_key)?;
		let result = ctx.ext.ecdsa_to_eth_address(&compressed_key);
		match result {
			Ok(eth_address) => {
				ctx.write_sandbox_memory(memory, out_ptr, eth_address.as_ref())?;
				Ok(ReturnCode::Success)
			},
			Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
		}

	/// Returns the number of times the currently executing contract exists on the call stack in
	/// addition to the calling instance.
	///
	/// # Return Value
	///
	fn reentrance_count(ctx: _, memory: _) -> Result<u32, TrapReason> {
		ctx.charge_gas(RuntimeCosts::ReentrantCount)?;
	}

	/// Returns the number of times specified contract exists on the call stack. Delegated calls are
	/// not counted as separate calls.
	///
	/// # Parameters
	///
	/// - `account_ptr`: a pointer to the contract address.
	///
	/// # Return Value
	///
	/// Returns `0` when the contract does not exist on the call stack.
	fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result<u32, TrapReason> {
		ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?;
		let account_id: <<E as Ext>::T as frame_system::Config>::AccountId =
			ctx.read_sandbox_memory_as(memory, account_ptr)?;
		Ok(ctx.ext.account_reentrance_count(&account_id))
	}

	/// Returns a nonce that is unique per contract instantiation.
	///
	/// The nonce is incremented for each successful contract instantiation. This is a
	/// sensible default salt for contract instantiations.
	fn instantiation_nonce(ctx: _, _memory: _) -> Result<u64, TrapReason> {
		ctx.charge_gas(RuntimeCosts::InstantationNonce)?;
		Ok(ctx.ext.nonce())
	}