#![cfg_attr(not(feature = "std"), no_std)]
use bitflags::bitflags;
use codec::{Decode, Encode};
use sp_core::Bytes;
use sp_runtime::{
traits::{Saturating, Zero},
DispatchError, RuntimeDebug,
};
use sp_std::prelude::*;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use sp_rpc::number::NumberOrHex;
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "std",
serde(
rename_all = "camelCase",
bound(serialize = "R: Serialize, Balance: Copy + Into<NumberOrHex>"),
bound(deserialize = "R: Deserialize<'de>, Balance: TryFrom<NumberOrHex>")
)
)]
pub struct ContractResult<R, Balance> {
pub gas_consumed: u64,
pub gas_required: u64,
pub storage_deposit: StorageDeposit<Balance>,
#[cfg_attr(feature = "std", serde(with = "as_string"))]
pub debug_message: Vec<u8>,
pub result: R,
}
pub type ContractExecResult<Balance> =
ContractResult<Result<ExecReturnValue, DispatchError>, Balance>;
pub type ContractInstantiateResult<AccountId, Balance> =
ContractResult<Result<InstantiateReturnValue<AccountId>, DispatchError>, Balance>;
pub type CodeUploadResult<CodeHash, Balance> =
Result<CodeUploadReturnValue<CodeHash, Balance>, DispatchError>;
pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
pub enum ContractAccessError {
DoesntExist,
}
bitflags! {
#[derive(Encode, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase", transparent))]
pub struct ReturnFlags: u32 {
const REVERT = 0x0000_0001;
}
}
#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct ExecReturnValue {
pub flags: ReturnFlags,
pub data: Bytes,
}
impl ExecReturnValue {
pub fn did_revert(&self) -> bool {
self.flags.contains(ReturnFlags::REVERT)
}
}
#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct InstantiateReturnValue<AccountId> {
pub result: ExecReturnValue,
pub account_id: AccountId,
}
#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "std",
serde(
rename_all = "camelCase",
bound(serialize = "CodeHash: Serialize, Balance: Copy + Into<NumberOrHex>"),
bound(deserialize = "CodeHash: Deserialize<'de>, Balance: TryFrom<NumberOrHex>")
)
)]
pub struct CodeUploadReturnValue<CodeHash, Balance> {
pub code_hash: CodeHash,
#[cfg_attr(feature = "std", serde(with = "as_hex"))]
pub deposit: Balance,
}
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub enum Code<Hash> {
Upload(Bytes),
Existing(Hash),
}
impl<T: Into<Vec<u8>>, Hash> From<T> for Code<Hash> {
fn from(from: T) -> Self {
Code::Upload(Bytes(from.into()))
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "std",
serde(
rename_all = "camelCase",
bound(serialize = "Balance: Copy + Into<NumberOrHex>"),
bound(deserialize = "Balance: TryFrom<NumberOrHex>")
)
)]
pub enum StorageDeposit<Balance> {
#[cfg_attr(feature = "std", serde(with = "as_hex"))]
Refund(Balance),
#[cfg_attr(feature = "std", serde(with = "as_hex"))]
Charge(Balance),
}
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: 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) -> Balance {
use StorageDeposit::*;
match self {
Charge(amount) => limit.saturating_sub(*amount),
Refund(amount) => limit.saturating_add(*amount),
}
}
}
#[cfg(feature = "std")]
mod as_string {
use super::*;
use serde::{ser::Error, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error> {
std::str::from_utf8(bytes)
.map_err(|e| S::Error::custom(format!("Debug buffer contains invalid UTF8: {}", e)))?
.serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
Ok(String::deserialize(deserializer)?.into_bytes())
}
}
#[cfg(feature = "std")]
mod as_hex {
use super::*;
use serde::{de::Error as _, Deserializer, Serializer};
pub fn serialize<S, Balance>(balance: &Balance, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
Balance: Copy + Into<NumberOrHex>,
{
Into::<NumberOrHex>::into(*balance).serialize(serializer)
}
pub fn deserialize<'de, D, Balance>(deserializer: D) -> Result<Balance, D::Error>
where
D: Deserializer<'de>,
Balance: TryFrom<NumberOrHex>,
{
Balance::try_from(NumberOrHex::deserialize(deserializer)?)
.map_err(|_| D::Error::custom("Cannot decode NumberOrHex to Balance"))
}
}