use std::rc::Rc;
use contract_transcode::{ContractMessageTranscoder, Value};
use parity_scale_codec::{Decode, Encode};
use crate::{
errors::MessageResult,
runtime::{minimal::RuntimeEvent, AccountIdFor, MinimalRuntime, Runtime},
session::{error::SessionError, BalanceOf},
EventRecordOf, Sandbox,
};
type ContractInstantiateResult<R> =
pallet_contracts::ContractInstantiateResult<AccountIdFor<R>, BalanceOf<R>, EventRecordOf<R>>;
type ContractExecResult<R> = pallet_contracts::ContractExecResult<BalanceOf<R>, EventRecordOf<R>>;
pub struct Record<T: pallet_contracts::Config> {
deploy_results: Vec<ContractInstantiateResult<T>>,
deploy_returns: Vec<AccountIdFor<T>>,
call_results: Vec<ContractExecResult<T>>,
call_returns: Vec<Vec<u8>>,
event_batches: Vec<EventBatch<T>>,
block_events_so_far: Option<usize>,
}
impl<T: pallet_contracts::Config> Default for Record<T> {
fn default() -> Self {
Self {
deploy_results: Vec::new(),
deploy_returns: Vec::new(),
call_results: Vec::new(),
call_returns: Vec::new(),
event_batches: Vec::new(),
block_events_so_far: None,
}
}
}
impl<T: pallet_contracts::Config> Record<T> {
pub(super) fn push_deploy_result(&mut self, result: ContractInstantiateResult<T>) {
self.deploy_results.push(result);
}
pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor<T>) {
self.deploy_returns.push(return_value);
}
pub(super) fn push_call_result(&mut self, result: ContractExecResult<T>) {
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 start_recording_events(
&mut self,
sandbox: &mut Sandbox<impl Runtime<Config = T>>,
) {
assert!(
self.block_events_so_far.is_none(),
"Already recording events"
);
self.block_events_so_far = Some(sandbox.events().len());
}
pub(super) fn stop_recording_events(
&mut self,
sandbox: &mut Sandbox<impl Runtime<Config = T>>,
) {
let start = self
.block_events_so_far
.take()
.expect("Not recording events");
let end = sandbox.events().len();
let events = sandbox.events()[start..end].to_vec();
self.event_batches.push(EventBatch { events });
}
}
impl<T: pallet_contracts::Config> Record<T> {
pub fn deploy_results(&self) -> &[ContractInstantiateResult<T>] {
&self.deploy_results
}
pub fn last_deploy_result(&self) -> &ContractInstantiateResult<T> {
self.deploy_results.last().expect("No deploy results")
}
pub fn deploy_returns(&self) -> &[AccountIdFor<T>] {
&self.deploy_returns
}
pub fn last_deploy_return(&self) -> &AccountIdFor<T> {
self.deploy_returns.last().expect("No deploy returns")
}
pub fn call_results(&self) -> &[ContractExecResult<T>] {
&self.call_results
}
pub fn last_call_result(&self) -> &ContractExecResult<T> {
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<V: Decode>(&self) -> Result<MessageResult<V>, 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<T>] {
&self.event_batches
}
pub fn last_event_batch(&self) -> &EventBatch<T> {
self.event_batches.last().expect("No event batches")
}
}
pub struct EventBatch<T: frame_system::Config> {
events: Vec<EventRecordOf<T>>,
}
impl<T: frame_system::Config> EventBatch<T> {
pub fn all_events(&self) -> &[EventRecordOf<T>] {
&self.events
}
}
impl EventBatch<MinimalRuntime> {
pub fn contract_events(&self) -> Vec<&[u8]> {
self.events
.iter()
.filter_map(|event| match &event.event {
RuntimeEvent::Contracts(
pallet_contracts::Event::<MinimalRuntime>::ContractEmitted { data, .. },
) => Some(data.as_slice()),
_ => None,
})
.collect()
}
pub fn contract_events_decoded(
&self,
transcoder: &Rc<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.into(), &mut &*data.encode())
{
return Some(decoded);
}
}
None
})
.collect()
}
}