use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use async_trait::async_trait;
use bytes::Bytes;
use xenith_core::{
ChainId, KeyMetadata, MessageId, MessageStatus, MessagingTransport, Result, SendOptions,
StateKey, StateValue, XenithError,
};
pub struct LayerZeroTransport {
pub endpoint_address: [u8; 20],
pub supported_chains: HashMap<ChainId, u32>,
next_message_id: Arc<AtomicU64>,
}
impl LayerZeroTransport {
pub fn new(endpoint_address: [u8; 20], chain_mappings: Vec<(ChainId, u32)>) -> Self {
Self {
endpoint_address,
supported_chains: chain_mappings.into_iter().collect(),
next_message_id: Arc::new(AtomicU64::new(1)),
}
}
}
#[async_trait]
impl MessagingTransport for LayerZeroTransport {
async fn send_message(
&self,
destination: ChainId,
_payload: Bytes,
_options: SendOptions,
) -> Result<MessageId> {
if !self.supported_chains.contains_key(&destination) {
return Err(XenithError::UnsupportedChain(destination));
}
let id = self.next_message_id.fetch_add(1, Ordering::Relaxed);
Ok(MessageId::from(id))
}
async fn estimate_fee(&self, _destination: ChainId, _payload: Bytes) -> Result<u128> {
Ok(100_000u128)
}
async fn message_status(&self, _id: MessageId) -> Result<MessageStatus> {
Ok(MessageStatus::Delivered)
}
fn sender_address(&self) -> Option<[u8; 20]> {
None
}
async fn poll_incoming(&self) -> Result<Vec<(StateKey, StateValue, Option<KeyMetadata>)>> {
Ok(vec![])
}
}
#[cfg(test)]
mod tests {
use super::*;
xenith_core::transport_compliance_tests!(LayerZeroTransport::new(
[0u8; 20],
vec![
(xenith_core::ChainId(1), 101u32),
(xenith_core::ChainId(42161), 110u32)
]
));
fn transport_with(chains: &[(u64, u32)]) -> LayerZeroTransport {
LayerZeroTransport::new(
[0u8; 20],
chains
.iter()
.map(|&(c, eid)| (ChainId::from(c), eid))
.collect(),
)
}
#[tokio::test]
async fn send_to_supported_chain_succeeds() {
let t = transport_with(&[(42161, 30110)]);
let id = t
.send_message(ChainId::from(42161), Bytes::new(), Default::default())
.await
.unwrap();
assert_eq!(id, MessageId::from(1));
}
#[tokio::test]
async fn message_ids_increment_per_send() {
let t = transport_with(&[(42161, 30110)]);
let a = t
.send_message(ChainId::from(42161), Bytes::new(), Default::default())
.await
.unwrap();
let b = t
.send_message(ChainId::from(42161), Bytes::new(), Default::default())
.await
.unwrap();
assert_eq!(a, MessageId::from(1));
assert_eq!(b, MessageId::from(2));
}
#[tokio::test]
async fn send_to_unsupported_chain_returns_error() {
let t = transport_with(&[(42161, 30110)]);
let err = t
.send_message(ChainId::from(1), Bytes::new(), Default::default())
.await
.unwrap_err();
assert!(matches!(err, XenithError::UnsupportedChain(ChainId(1))));
}
#[tokio::test]
async fn estimate_fee_returns_stub_value() {
let t = transport_with(&[]);
assert_eq!(
t.estimate_fee(ChainId::from(1), Bytes::new())
.await
.unwrap(),
100_000u128
);
}
#[tokio::test]
async fn message_status_returns_delivered() {
let t = transport_with(&[]);
assert!(matches!(
t.message_status(MessageId::from(99)).await.unwrap(),
MessageStatus::Delivered
));
}
}