use std::sync::Arc;
use contract_transcode::{ContractMessageTranscoder, Value};
use frame_system::Config as SysConfig;
use ink_sandbox::{pallet_contracts, AccountIdFor, EventRecordOf};
use parity_scale_codec::{Decode, Encode};
use crate::{
errors::MessageResult,
minimal::{MinimalSandboxRuntime, RuntimeEvent},
session::{error::SessionError, BalanceOf},
};
type ContractInstantiateResult<R> =
pallet_contracts::ContractInstantiateResult<AccountIdFor<R>, BalanceOf<R>, EventRecordOf<R>>;
type ContractExecResult<R> = pallet_contracts::ContractExecResult<BalanceOf<R>, EventRecordOf<R>>;
#[derive(frame_support::DefaultNoBound)]
pub struct Record<Config: pallet_contracts::Config> {
deploy_results: Vec<ContractInstantiateResult<Config>>,
deploy_returns: Vec<AccountIdFor<Config>>,
call_results: Vec<ContractExecResult<Config>>,
call_returns: Vec<Vec<u8>>,
event_batches: Vec<EventBatch<Config>>,
}
impl<Config: pallet_contracts::Config> Record<Config> {
pub(super) fn push_deploy_result(&mut self, result: ContractInstantiateResult<Config>) {
self.deploy_results.push(result);
}
pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor<Config>) {
self.deploy_returns.push(return_value);
}
pub(super) fn push_call_result(&mut self, result: ContractExecResult<Config>) {
self.call_results.push(result);
}
pub(super) fn push_call_return(&mut self, return_value: Vec<u8>) {
self.call_returns.push(return_value);
}
pub(super) fn push_event_batches(&mut self, events: Vec<EventRecordOf<Config>>) {
self.event_batches.push(EventBatch { events });
}
}
impl<Config: pallet_contracts::Config> Record<Config> {
pub fn deploy_results(&self) -> &[ContractInstantiateResult<Config>] {
&self.deploy_results
}
pub fn last_deploy_result(&self) -> &ContractInstantiateResult<Config> {
self.deploy_results.last().expect("No deploy results")
}
pub fn deploy_returns(&self) -> &[AccountIdFor<Config>] {
&self.deploy_returns
}
pub fn last_deploy_return(&self) -> &AccountIdFor<Config> {
self.deploy_returns.last().expect("No deploy returns")
}
pub fn call_results(&self) -> &[ContractExecResult<Config>] {
&self.call_results
}
pub fn last_call_result(&self) -> &ContractExecResult<Config> {
self.call_results.last().expect("No call results")
}
pub fn call_returns(&self) -> &[Vec<u8>] {
&self.call_returns
}
pub fn last_call_return(&self) -> &[u8] {
self.call_returns.last().expect("No call returns")
}
pub fn last_call_return_decoded<T: Decode>(&self) -> Result<MessageResult<T>, SessionError> {
let mut raw = self.last_call_return();
MessageResult::decode(&mut raw).map_err(|err| {
SessionError::Decoding(format!(
"Failed to decode the result of calling a contract: {err:?}"
))
})
}
pub fn event_batches(&self) -> &[EventBatch<Config>] {
&self.event_batches
}
pub fn last_event_batch(&self) -> &EventBatch<Config> {
self.event_batches.last().expect("No event batches")
}
}
pub struct EventBatch<R: SysConfig> {
events: Vec<EventRecordOf<R>>,
}
impl<R: SysConfig> EventBatch<R> {
pub fn all_events(&self) -> &[EventRecordOf<R>] {
&self.events
}
}
impl EventBatch<MinimalSandboxRuntime> {
pub fn contract_events(&self) -> Vec<&[u8]> {
self.events
.iter()
.filter_map(|event| match &event.event {
RuntimeEvent::Contracts(
pallet_contracts::Event::<MinimalSandboxRuntime>::ContractEmitted {
data, ..
},
) => Some(data.as_slice()),
_ => None,
})
.collect()
}
pub fn contract_events_decoded(
&self,
transcoder: &Arc<ContractMessageTranscoder>,
) -> Vec<Value> {
let signature_topics = transcoder
.metadata()
.spec()
.events()
.iter()
.filter_map(|event| event.signature_topic())
.map(|sig| sig.as_bytes().try_into().unwrap())
.collect::<Vec<[u8; 32]>>();
self.contract_events()
.into_iter()
.filter_map(|data| {
for signature_topic in &signature_topics {
if let Ok(decoded) = transcoder
.decode_contract_event(&signature_topic, &mut &*data.encode())
{
return Some(decoded);
}
}
None
})
.collect()
}
}