snowbridge_inbound_queue_primitives/v2/
message.rsuse crate::{v2::IGatewayV2::Payload, Log};
use alloy_core::{
primitives::B256,
sol,
sol_types::{SolEvent, SolType},
};
use codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_core::{RuntimeDebug, H160, H256};
use sp_std::prelude::*;
sol! {
interface IGatewayV2 {
struct AsNativeTokenERC20 {
address token_id;
uint128 value;
}
struct AsForeignTokenERC20 {
bytes32 token_id;
uint128 value;
}
struct EthereumAsset {
uint8 kind;
bytes data;
}
struct Xcm {
uint8 kind;
bytes data;
}
struct XcmCreateAsset {
address token;
uint8 network;
}
struct Payload {
address origin;
EthereumAsset[] assets;
Xcm xcm;
bytes claimer;
uint128 value;
uint128 executionFee;
uint128 relayerFee;
}
event OutboundMessageAccepted(uint64 nonce, Payload payload);
}
}
impl core::fmt::Debug for IGatewayV2::Payload {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Payload")
.field("origin", &self.origin)
.field("assets", &self.assets)
.field("xcm", &self.xcm)
.field("claimer", &self.claimer)
.field("value", &self.value)
.field("executionFee", &self.executionFee)
.field("relayerFee", &self.relayerFee)
.finish()
}
}
impl core::fmt::Debug for IGatewayV2::EthereumAsset {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EthereumAsset")
.field("kind", &self.kind)
.field("data", &self.data)
.finish()
}
}
impl core::fmt::Debug for IGatewayV2::Xcm {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Xcm")
.field("kind", &self.kind)
.field("data", &self.data)
.finish()
}
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum XcmPayload {
Raw(Vec<u8>),
CreateAsset { token: H160, network: Network },
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub enum Network {
Polkadot,
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct Message {
pub gateway: H160,
pub nonce: u64,
pub origin: H160,
pub assets: Vec<EthereumAsset>,
pub xcm: XcmPayload,
pub claimer: Option<Vec<u8>>,
pub value: u128,
pub execution_fee: u128,
pub relayer_fee: u128,
}
#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum EthereumAsset {
NativeTokenERC20 {
token_id: H160,
value: u128,
},
ForeignTokenERC20 {
token_id: H256,
value: u128,
},
}
#[derive(Copy, Clone, RuntimeDebug)]
pub struct MessageDecodeError;
impl TryFrom<&Log> for Message {
type Error = MessageDecodeError;
fn try_from(log: &Log) -> Result<Self, Self::Error> {
let topics: Vec<B256> = log.topics.iter().map(|x| B256::from_slice(x.as_ref())).collect();
let event = IGatewayV2::OutboundMessageAccepted::decode_raw_log(topics, &log.data, true)
.map_err(|_| MessageDecodeError)?;
let payload = event.payload;
let substrate_assets = Self::extract_assets(&payload)?;
let xcm = XcmPayload::try_from(&payload)?;
let mut claimer = None;
if payload.claimer.len() > 0 {
claimer = Some(payload.claimer.to_vec());
}
let message = Message {
gateway: log.address,
nonce: event.nonce,
origin: H160::from(payload.origin.as_ref()),
assets: substrate_assets,
xcm,
claimer,
value: payload.value,
execution_fee: payload.executionFee,
relayer_fee: payload.relayerFee,
};
Ok(message)
}
}
impl Message {
fn extract_assets(
payload: &IGatewayV2::Payload,
) -> Result<Vec<EthereumAsset>, MessageDecodeError> {
let mut substrate_assets = vec![];
for asset in &payload.assets {
substrate_assets.push(EthereumAsset::try_from(asset)?);
}
Ok(substrate_assets)
}
}
impl TryFrom<&IGatewayV2::Payload> for XcmPayload {
type Error = MessageDecodeError;
fn try_from(payload: &Payload) -> Result<Self, Self::Error> {
let xcm = match payload.xcm.kind {
0 => XcmPayload::Raw(payload.xcm.data.to_vec()),
1 => {
let create_asset = IGatewayV2::XcmCreateAsset::abi_decode(&payload.xcm.data, true)
.map_err(|_| MessageDecodeError)?;
let network = match create_asset.network {
0 => Network::Polkadot,
_ => return Err(MessageDecodeError),
};
XcmPayload::CreateAsset { token: H160::from(create_asset.token.as_ref()), network }
},
_ => return Err(MessageDecodeError),
};
Ok(xcm)
}
}
impl TryFrom<&IGatewayV2::EthereumAsset> for EthereumAsset {
type Error = MessageDecodeError;
fn try_from(asset: &IGatewayV2::EthereumAsset) -> Result<EthereumAsset, Self::Error> {
let asset = match asset.kind {
0 => {
let native_data = IGatewayV2::AsNativeTokenERC20::abi_decode(&asset.data, true)
.map_err(|_| MessageDecodeError)?;
EthereumAsset::NativeTokenERC20 {
token_id: H160::from(native_data.token_id.as_ref()),
value: native_data.value,
}
},
1 => {
let foreign_data = IGatewayV2::AsForeignTokenERC20::abi_decode(&asset.data, true)
.map_err(|_| MessageDecodeError)?;
EthereumAsset::ForeignTokenERC20 {
token_id: H256::from(foreign_data.token_id.as_ref()),
value: foreign_data.value,
}
},
_ => return Err(MessageDecodeError),
};
Ok(asset)
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::assert_ok;
use hex_literal::hex;
use sp_core::H160;
#[test]
fn test_decode() {
let log = Log{
address: hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305").into(),
topics: vec![hex!("550e2067494b1736ea5573f2d19cdc0ac95b410fff161bf16f11c6229655ec9c").into()],
data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(),
};
let result = Message::try_from(&log);
assert_ok!(result.clone());
let message = result.unwrap();
assert_eq!(H160::from(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305")), message.gateway);
assert_eq!(H160::from(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305")), message.origin);
}
}