use core::marker::PhantomData;
use alloc::vec::Vec;
use codec::{Decode, Encode};
use frame_support::{
dispatch::{DispatchResultWithPostInfo, Pays, PostDispatchInfo},
traits::{Currency, ExistenceRequirement, Get},
};
use impl_trait_for_tuples::impl_for_tuples;
use ismp::messaging::{Message, MessageWithWeight};
use polkadot_sdk::{
frame_support::{weights::WeightToFee, PalletId},
sp_runtime::{traits::AccountIdConversion, Weight},
*,
};
use sp_runtime::{
traits::{MaybeDisplay, Member, Zero},
DispatchError,
};
use crypto_utils::verification::Signature;
use ismp::events::Event;
pub trait FeeHandler {
fn on_executed(
messages: Vec<MessageWithWeight>,
events: Vec<Event>,
) -> DispatchResultWithPostInfo;
}
pub struct WeightFeeHandler<AccountId, C, W, T, const POLICY: bool>(
PhantomData<(AccountId, C, W, T)>,
);
type BalanceOf<C, AccountId> = <C as Currency<AccountId>>::Balance;
impl<AccountId, C, W, T, const POLICY: bool> FeeHandler
for WeightFeeHandler<AccountId, C, W, T, POLICY>
where
AccountId: Member + MaybeDisplay + Decode + Encode,
C: Currency<AccountId>,
W: WeightToFee<Balance = BalanceOf<C, AccountId>>,
T: Get<PalletId>,
{
fn on_executed(
messages: Vec<MessageWithWeight>,
_events: Vec<Event>,
) -> DispatchResultWithPostInfo {
if !POLICY {
return Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::No })
}
let mut total_weight = Weight::zero();
let treasury_account: AccountId = T::get().into_account_truncating();
for message in &messages {
let weight = message.weight;
total_weight.saturating_accrue(weight);
let fee = W::weight_to_fee(&weight);
if fee.is_zero() {
continue
}
let originator = match message.message.clone() {
Message::Request(msg) => {
let data = sp_io::hashing::keccak_256(&msg.requests.encode());
Signature::decode(&mut &msg.signer[..])
.ok()
.and_then(|sig| sig.verify_and_get_sr25519_pubkey(&data, None).ok())
},
Message::Response(msg) => {
let data = sp_io::hashing::keccak_256(&msg.datagram.encode());
Signature::decode(&mut &msg.signer[..])
.ok()
.and_then(|sig| sig.verify_and_get_sr25519_pubkey(&data, None).ok())
},
_ => None,
};
if let Some(originator_bytes) = originator {
if let Ok(account) = AccountId::decode(&mut &originator_bytes[..]) {
C::transfer(&account, &treasury_account, fee, ExistenceRequirement::KeepAlive)
.map_err(|_| DispatchError::Other("Failed to transfer fee to treasury"))?;
}
}
}
Ok(PostDispatchInfo { actual_weight: Some(total_weight), pays_fee: Pays::Yes })
}
}
#[impl_for_tuples(5)]
impl FeeHandler for TupleIdentifier {
fn on_executed(
messages: Vec<MessageWithWeight>,
events: Vec<Event>,
) -> DispatchResultWithPostInfo {
for_tuples!( #(
<TupleIdentifier as FeeHandler>::on_executed(messages.clone(), events.clone())?;
)* );
Ok(Default::default())
}
}