use std::{
fmt,
fmt::Debug,
marker::PhantomData,
};
use frame_support::pallet_prelude::{
Decode,
Encode,
};
use ink::codegen::ContractCallBuilder;
use ink_env::{
Environment,
call::{
FromAddr,
utils::DecodeMessageResult,
},
};
use ink_primitives::{
Address,
ConstructorResult,
H256,
MessageResult,
};
use ink_revive_types::{
CodeUploadResult,
ExecReturnValue,
InstantiateReturnValue,
StorageDeposit,
evm::CallTrace,
};
use sp_runtime::{
DispatchError,
Weight,
};
pub type ContractInstantiateResultFor<E> =
ContractResult<InstantiateReturnValue, <E as Environment>::Balance>;
#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode)]
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>,
}
pub type ContractExecResultFor<E> =
ContractResult<ExecReturnValue, <E as Environment>::Balance>;
pub struct BareInstantiationResult<E: Environment, EventLog> {
pub addr: Address,
pub account_id: E::AccountId,
pub events: EventLog,
pub trace: Option<CallTrace>,
pub code_hash: H256,
}
impl<E: Environment, EventLog> Debug for BareInstantiationResult<E, EventLog>
where
EventLog: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("BareInstantiationResult")
.field("addr", &self.addr)
.field("account_id", &self.account_id.encode())
.field("events", &self.events)
.field("trace", &self.trace)
.field("code_hash", &self.code_hash)
.finish()
}
}
pub struct InstantiationResult<E: Environment, EventLog, Abi> {
pub addr: Address,
pub account_id: E::AccountId,
pub dry_run: InstantiateDryRunResult<E, Abi>,
pub events: EventLog,
pub trace: Option<CallTrace>,
pub code_hash: H256,
}
impl<E: Environment, EventLog, Abi> InstantiationResult<E, EventLog, Abi> {
pub fn call_builder<Contract>(&self) -> <Contract as ContractCallBuilder>::Type<Abi>
where
Contract: ContractCallBuilder,
<Contract as ContractCallBuilder>::Type<Abi>: FromAddr,
{
<<Contract as ContractCallBuilder>::Type<Abi> as FromAddr>::from_addr(self.addr)
}
pub fn call_builder_abi<Contract, CallAbi>(
&self,
) -> <Contract as ContractCallBuilder>::Type<CallAbi>
where
Contract: ContractCallBuilder,
<Contract as ContractCallBuilder>::Type<CallAbi>: FromAddr,
{
<<Contract as ContractCallBuilder>::Type<CallAbi> as FromAddr>::from_addr(
self.addr,
)
}
}
impl<E: Environment, EventLog, Abi> Debug for InstantiationResult<E, EventLog, Abi>
where
E::AccountId: Debug,
E::Balance: Debug,
E::EventRecord: Debug,
EventLog: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("InstantiationResult")
.field("addr", &self.addr)
.field("dry_run", &self.dry_run)
.field("events", &self.events)
.finish()
}
}
pub struct UploadResult<E: Environment, EventLog> {
pub code_hash: H256,
pub dry_run: CodeUploadResult<E::Balance>,
pub events: EventLog,
}
impl<E: Environment, EventLog> Debug for UploadResult<E, EventLog>
where
E::Balance: Debug,
H256: Debug,
EventLog: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("UploadResult")
.field("code_hash", &self.code_hash)
.field("dry_run", &self.dry_run)
.field("events", &self.events)
.finish()
}
}
pub struct CallResult<E: Environment, V, EventLog, Abi> {
pub dry_run: CallDryRunResult<E, V, Abi>,
pub events: EventLog,
pub trace: Option<CallTrace>,
}
impl<E: Environment, V: DecodeMessageResult<Abi>, EventLog, Abi>
CallResult<E, V, EventLog, Abi>
{
pub fn message_result(&self) -> MessageResult<V> {
self.dry_run.message_result()
}
pub fn return_value(self) -> V {
self.dry_run.return_value()
}
}
impl<E: Environment, V, EventLog, Abi> CallResult<E, V, EventLog, Abi> {
pub fn return_data(&self) -> &[u8] {
&self.dry_run.exec_return_value().data
}
pub fn extract_error(&self) -> Option<String> {
if !self.dry_run.did_revert() {
return None;
}
if let Some(trace) = &self.trace {
for call in &trace.calls {
if let Some(error) = &call.error {
return Some(error.clone());
}
}
if let Some(error) = &trace.error {
return Some(error.clone());
}
}
Some(format!("{:?}", self.return_data()))
}
}
impl<E: Environment, V, EventLog, Abi> Debug for CallResult<E, V, EventLog, Abi>
where
E: Debug,
E::Balance: Debug,
E::EventRecord: Debug,
V: Debug,
EventLog: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("CallResult")
.field("dry_run", &self.dry_run)
.field("events", &self.events)
.field("trace", &self.trace)
.finish()
}
}
pub struct CallDryRunResult<E: Environment, V, Abi> {
pub exec_result: ContractExecResultFor<E>,
pub trace: Option<CallTrace>,
pub _marker: PhantomData<(V, Abi)>,
}
impl<E: Environment, V, Abi> Debug for CallDryRunResult<E, V, Abi>
where
E::Balance: Debug,
E::EventRecord: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("CallDryRunResult")
.field("exec_result", &self.exec_result)
.field("trace", &self.trace)
.finish()
}
}
impl<E: Environment, V, Abi> CallDryRunResult<E, V, Abi> {
pub fn is_err(&self) -> bool {
self.exec_result.result.is_err() || self.did_revert()
}
pub fn exec_return_value(&self) -> &ExecReturnValue {
self.exec_result
.result
.as_ref()
.unwrap_or_else(|call_err| panic!("Call dry-run failed: {call_err:?}"))
}
pub fn did_revert(&self) -> bool {
let res = self.exec_result.result.clone().expect("no result found");
res.did_revert()
}
pub fn return_data(&self) -> &[u8] {
&self.exec_return_value().data
}
}
impl<E: Environment, V: DecodeMessageResult<Abi>, Abi> CallDryRunResult<E, V, Abi> {
pub fn message_result(&self) -> MessageResult<V> {
let data = &self.exec_return_value().data;
DecodeMessageResult::decode_output(data.as_ref(), self.did_revert()).unwrap_or_else(|env_err| {
panic!(
"Decoding dry run result to ink! message return type failed: {env_err:?} {:?}\n\n\
Attempt to stringify returned data: {:?}",
self.exec_return_value(),
String::from_utf8_lossy(&self.exec_return_value().data[..])
)
})
}
pub fn return_value(&self) -> V {
self.message_result()
.unwrap_or_else(|lang_err| {
panic!(
"Encountered a `LangError` while decoding dry run result to ink! message: {lang_err:?}"
)
})
}
}
#[derive(Clone)]
pub struct InstantiateDryRunResult<E: Environment, Abi> {
pub contract_result: ContractInstantiateResultFor<E>,
pub _marker: PhantomData<Abi>,
}
impl<E: Environment, Abi> From<ContractInstantiateResultFor<E>>
for InstantiateDryRunResult<E, Abi>
{
fn from(contract_result: ContractInstantiateResultFor<E>) -> Self {
Self {
contract_result,
_marker: PhantomData,
}
}
}
impl<E: Environment, Abi> InstantiateDryRunResult<E, Abi> {
pub fn is_err(&self) -> bool {
self.contract_result.result.is_err() || self.did_revert()
}
pub fn instantiate_return_value(&self) -> &InstantiateReturnValue {
self.contract_result
.result
.as_ref()
.unwrap_or_else(|call_err| panic!("Instantiate dry-run failed: {call_err:?}"))
}
pub fn constructor_result<V: DecodeMessageResult<Abi>>(
&self,
) -> ConstructorResult<V> {
let data = &self.instantiate_return_value().result.data;
DecodeMessageResult::decode_output(data.as_ref(), self.did_revert()).unwrap_or_else(|env_err| {
panic!("Decoding dry run result to constructor return type failed: {env_err:?}")
})
}
pub fn return_data(&self) -> &[u8] {
&self.instantiate_return_value().result.data
}
pub fn did_revert(&self) -> bool {
let res = self.instantiate_return_value().clone().result;
res.did_revert()
}
}
impl<E, Abi> Debug for InstantiateDryRunResult<E, Abi>
where
E: Environment,
E::AccountId: Debug,
E::Balance: Debug,
E::EventRecord: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InstantiateDryRunResult")
.field("contract_result", &self.contract_result)
.finish()
}
}