use crate::{
evm::DryRunConfig, mock::MockHandler, storage::WriteOutcome, BalanceOf, Config, Time, H160,
U256,
};
use alloc::{boxed::Box, fmt::Debug, string::String, vec::Vec};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{traits::tokens::Balance, weights::Weight};
use pallet_revive_uapi::ReturnFlags;
use scale_info::TypeInfo;
use sp_core::Get;
use sp_runtime::{
traits::{One, Saturating, Zero},
DispatchError, RuntimeDebug,
};
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct ContractResult<R, Balance> {
pub weight_consumed: Weight,
pub weight_required: Weight,
pub storage_deposit: StorageDeposit<Balance>,
pub max_storage_deposit: StorageDeposit<Balance>,
pub gas_consumed: Balance,
pub result: Result<R, DispatchError>,
}
impl<R: Default, B: Balance> Default for ContractResult<R, B> {
fn default() -> Self {
Self {
weight_consumed: Default::default(),
weight_required: Default::default(),
storage_deposit: Default::default(),
max_storage_deposit: Default::default(),
gas_consumed: Default::default(),
result: Ok(Default::default()),
}
}
}
#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct EthTransactInfo<Balance> {
pub weight_required: Weight,
pub storage_deposit: Balance,
pub max_storage_deposit: Balance,
pub eth_gas: U256,
pub data: Vec<u8>,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum EthTransactError {
Data(Vec<u8>),
Message(String),
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum BalanceConversionError {
Value,
Dust,
}
#[derive(Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct BalanceWithDust<Balance> {
value: Balance,
dust: u32,
}
impl<Balance> From<Balance> for BalanceWithDust<Balance> {
fn from(value: Balance) -> Self {
Self { value, dust: 0 }
}
}
impl<Balance> BalanceWithDust<Balance> {
pub fn deconstruct(self) -> (Balance, u32) {
(self.value, self.dust)
}
pub fn new_unchecked<T: Config>(value: Balance, dust: u32) -> Self {
debug_assert!(dust < T::NativeToEthRatio::get());
Self { value, dust }
}
pub fn from_value<T: Config>(
value: U256,
) -> Result<BalanceWithDust<BalanceOf<T>>, BalanceConversionError> {
if value.is_zero() {
return Ok(Default::default())
}
let (quotient, remainder) = value.div_mod(T::NativeToEthRatio::get().into());
let value = quotient.try_into().map_err(|_| BalanceConversionError::Value)?;
let dust = remainder.try_into().map_err(|_| BalanceConversionError::Dust)?;
Ok(BalanceWithDust { value, dust })
}
}
impl<Balance: Zero + One + Saturating> BalanceWithDust<Balance> {
pub fn is_zero(&self) -> bool {
self.value.is_zero() && self.dust == 0
}
pub fn into_rounded_balance(self) -> Balance {
if self.dust == 0 {
self.value
} else {
self.value.saturating_add(Balance::one())
}
}
}
pub type CodeUploadResult<Balance> = Result<CodeUploadReturnValue<Balance>, DispatchError>;
pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
pub type SetStorageResult = Result<WriteOutcome, ContractAccessError>;
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub enum ContractAccessError {
DoesntExist,
KeyDecodingFailed,
StorageWriteFailed(DispatchError),
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
pub struct ExecReturnValue {
pub flags: ReturnFlags,
pub data: Vec<u8>,
}
impl ExecReturnValue {
pub fn did_revert(&self) -> bool {
self.flags.contains(ReturnFlags::REVERT)
}
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
pub struct InstantiateReturnValue {
pub result: ExecReturnValue,
pub addr: H160,
}
#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub struct CodeUploadReturnValue<Balance> {
pub code_hash: sp_core::H256,
pub deposit: Balance,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum Code {
Upload(Vec<u8>),
Existing(sp_core::H256),
}
#[derive(
Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo,
)]
pub enum StorageDeposit<Balance> {
Refund(Balance),
Charge(Balance),
}
impl<T, Balance> ContractResult<T, Balance> {
pub fn map_result<V>(self, map_fn: impl FnOnce(T) -> V) -> ContractResult<V, Balance> {
ContractResult {
weight_consumed: self.weight_consumed,
weight_required: self.weight_required,
storage_deposit: self.storage_deposit,
max_storage_deposit: self.max_storage_deposit,
gas_consumed: self.gas_consumed,
result: self.result.map(map_fn),
}
}
}
impl<Balance: Zero> Default for StorageDeposit<Balance> {
fn default() -> Self {
Self::Charge(Zero::zero())
}
}
impl<Balance: Zero + Copy> StorageDeposit<Balance> {
pub fn charge_or_zero(&self) -> Balance {
match self {
Self::Charge(amount) => *amount,
Self::Refund(_) => Zero::zero(),
}
}
pub fn is_zero(&self) -> bool {
match self {
Self::Charge(amount) => amount.is_zero(),
Self::Refund(amount) => amount.is_zero(),
}
}
}
impl<Balance> StorageDeposit<Balance>
where
Balance: frame_support::traits::tokens::Balance + Saturating + Ord + Copy,
{
pub fn saturating_add(&self, rhs: &Self) -> Self {
use StorageDeposit::*;
match (self, rhs) {
(Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)),
(Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)),
(Charge(lhs), Refund(rhs)) =>
if lhs >= rhs {
Charge(lhs.saturating_sub(*rhs))
} else {
Refund(rhs.saturating_sub(*lhs))
},
(Refund(lhs), Charge(rhs)) =>
if lhs > rhs {
Refund(lhs.saturating_sub(*rhs))
} else {
Charge(rhs.saturating_sub(*lhs))
},
}
}
pub fn saturating_sub(&self, rhs: &Self) -> Self {
use StorageDeposit::*;
match (self, rhs) {
(Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)),
(Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)),
(Charge(lhs), Charge(rhs)) =>
if lhs >= rhs {
Charge(lhs.saturating_sub(*rhs))
} else {
Refund(rhs.saturating_sub(*lhs))
},
(Refund(lhs), Refund(rhs)) =>
if lhs > rhs {
Refund(lhs.saturating_sub(*rhs))
} else {
Charge(rhs.saturating_sub(*lhs))
},
}
}
pub fn available(&self, limit: &Balance) -> Option<Balance> {
use StorageDeposit::*;
match self {
Charge(amount) => limit.checked_sub(amount),
Refund(amount) => Some(limit.saturating_add(*amount)),
}
}
}
pub struct ExecConfig<T: Config> {
pub bump_nonce: bool,
pub collect_deposit_from_hold: Option<(u32, Weight)>,
pub effective_gas_price: Option<U256>,
pub is_dry_run: Option<DryRunConfig<<<T as Config>::Time as Time>::Moment>>,
pub mock_handler: Option<Box<dyn MockHandler<T>>>,
}
impl<T: Config> ExecConfig<T> {
pub fn new_substrate_tx() -> Self {
Self {
bump_nonce: true,
collect_deposit_from_hold: None,
effective_gas_price: None,
is_dry_run: None,
mock_handler: None,
}
}
pub fn new_substrate_tx_without_bump() -> Self {
Self {
bump_nonce: false,
collect_deposit_from_hold: None,
effective_gas_price: None,
mock_handler: None,
is_dry_run: None,
}
}
pub fn new_eth_tx(effective_gas_price: U256, encoded_len: u32, base_weight: Weight) -> Self {
Self {
bump_nonce: false,
collect_deposit_from_hold: Some((encoded_len, base_weight)),
effective_gas_price: Some(effective_gas_price),
mock_handler: None,
is_dry_run: None,
}
}
pub fn with_dry_run(
mut self,
dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
) -> Self {
self.is_dry_run = Some(dry_run_config);
self
}
#[cfg(test)]
pub fn clone(&self) -> Self {
Self {
bump_nonce: self.bump_nonce,
collect_deposit_from_hold: self.collect_deposit_from_hold,
effective_gas_price: self.effective_gas_price,
is_dry_run: self.is_dry_run.clone(),
mock_handler: None,
}
}
}
#[must_use = "You must handle whether the code was removed or not."]
pub enum CodeRemoved {
No,
Yes,
}