#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
use core::fmt::Debug;
use cfg_traits::liquidity_pools::{LpMessageForwarded, MessageReceiver, MessageSender};
use cfg_types::domain_address::{Domain, DomainAddress};
use frame_support::{dispatch::DispatchResult, pallet_prelude::*};
use frame_system::pallet_prelude::OriginFor;
pub use pallet::*;
use parity_scale_codec::FullCodec;
use sp_core::H160;
use sp_std::convert::TryInto;
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct ForwardInfo {
pub(crate) source_domain: Domain,
pub(crate) contract: H160,
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type Message: LpMessageForwarded<Domain = Domain>
+ Clone
+ Debug
+ PartialEq
+ Eq
+ MaxEncodedLen
+ TypeInfo
+ FullCodec;
type MessageSender: MessageSender<
Middleware = Self::RouterId,
Origin = DomainAddress,
Message = Self::Message,
>;
type MessageReceiver: MessageReceiver<
Middleware = Self::RouterId,
Origin = DomainAddress,
Message = Self::Message,
>;
type RouterId: Parameter + MaxEncodedLen;
}
#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
ForwarderSet {
router_id: T::RouterId,
source_domain: Domain,
forwarding_contract: H160,
},
ForwarderRemoved {
router_id: T::RouterId,
source_domain: Domain,
forwarding_contract: H160,
},
}
#[pallet::storage]
pub type RouterForwarding<T: Config> =
StorageMap<_, Blake2_128Concat, T::RouterId, ForwardInfo, OptionQuery>;
#[pallet::error]
pub enum Error<T> {
ForwardInfoNotFound,
UnwrappingFailed,
SourceDomainMismatch,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(T::DbWeight::get().writes(1))]
#[pallet::call_index(0)]
pub fn set_forwarder(
origin: OriginFor<T>,
router_id: T::RouterId,
source_domain: Domain,
forwarding_contract: H160,
) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?;
RouterForwarding::<T>::insert(
&router_id,
ForwardInfo {
source_domain,
contract: forwarding_contract,
},
);
Self::deposit_event(Event::<T>::ForwarderSet {
router_id,
source_domain,
forwarding_contract,
});
Ok(())
}
#[pallet::weight(T::DbWeight::get().writes(1))]
#[pallet::call_index(1)]
pub fn remove_forwarder(origin: OriginFor<T>, router_id: T::RouterId) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?;
RouterForwarding::<T>::take(&router_id)
.map(|info| {
Self::deposit_event(Event::<T>::ForwarderRemoved {
router_id,
source_domain: info.source_domain,
forwarding_contract: info.contract,
});
})
.ok_or(Error::<T>::ForwardInfoNotFound.into())
}
}
impl<T: Config> MessageSender for Pallet<T> {
type Message = T::Message;
type Middleware = T::RouterId;
type Origin = DomainAddress;
fn send(
router_id: T::RouterId,
origin: DomainAddress,
message: T::Message,
) -> DispatchResult {
let msg = RouterForwarding::<T>::get(&router_id)
.map(|info| {
T::Message::try_wrap_forward(info.source_domain, info.contract, message.clone())
})
.unwrap_or_else(|| {
ensure!(!message.is_forwarded(), Error::<T>::ForwardInfoNotFound);
Ok(message)
})?;
T::MessageSender::send(router_id, origin, msg)
}
}
impl<T: Config> MessageReceiver for Pallet<T> {
type Message = T::Message;
type Middleware = T::RouterId;
type Origin = DomainAddress;
fn receive(
router_id: T::RouterId,
forwarding_domain_address: DomainAddress,
message: T::Message,
) -> DispatchResult {
let (lp_message, domain_address) = match (
RouterForwarding::<T>::get(&router_id),
message.clone().unwrap_forwarded(),
) {
(Some(info), Some((source_domain, _contract, lp_message))) => {
ensure!(
info.source_domain == source_domain,
Error::<T>::SourceDomainMismatch
);
let domain_address = DomainAddress::Evm(
info.source_domain
.get_evm_chain_id()
.expect("Domain not Centrifuge; qed"),
info.contract,
);
Ok((lp_message, domain_address))
}
(Some(_), None) => Err(Error::<T>::UnwrappingFailed),
(None, None) => Ok((message, forwarding_domain_address)),
(None, Some((_, _, _))) => Err(Error::<T>::ForwardInfoNotFound),
}
.map_err(|e: Error<T>| e)?;
T::MessageReceiver::receive(router_id, domain_address, lp_message)
}
}
}