snowbridge_inbound_queue_primitives/v2/
message.rs1use crate::{v2::IGatewayV2::Payload, Log};
6use alloy_core::{
7 primitives::B256,
8 sol,
9 sol_types::{SolEvent, SolType},
10};
11use codec::{Decode, Encode};
12use scale_info::TypeInfo;
13use sp_core::{RuntimeDebug, H160, H256};
14use sp_std::prelude::*;
15
16sol! {
17 interface IGatewayV2 {
18 struct AsNativeTokenERC20 {
19 address token_id;
20 uint128 value;
21 }
22 struct AsForeignTokenERC20 {
23 bytes32 token_id;
24 uint128 value;
25 }
26 struct EthereumAsset {
27 uint8 kind;
28 bytes data;
29 }
30 struct Xcm {
31 uint8 kind;
32 bytes data;
33 }
34 struct XcmCreateAsset {
35 address token;
36 uint8 network;
37 }
38 struct Payload {
39 address origin;
40 EthereumAsset[] assets;
41 Xcm xcm;
42 bytes claimer;
43 uint128 value;
44 uint128 executionFee;
45 uint128 relayerFee;
46 }
47 event OutboundMessageAccepted(uint64 nonce, Payload payload);
48 }
49}
50
51impl core::fmt::Debug for IGatewayV2::Payload {
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53 f.debug_struct("Payload")
54 .field("origin", &self.origin)
55 .field("assets", &self.assets)
56 .field("xcm", &self.xcm)
57 .field("claimer", &self.claimer)
58 .field("value", &self.value)
59 .field("executionFee", &self.executionFee)
60 .field("relayerFee", &self.relayerFee)
61 .finish()
62 }
63}
64
65impl core::fmt::Debug for IGatewayV2::EthereumAsset {
66 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67 f.debug_struct("EthereumAsset")
68 .field("kind", &self.kind)
69 .field("data", &self.data)
70 .finish()
71 }
72}
73
74impl core::fmt::Debug for IGatewayV2::Xcm {
75 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76 f.debug_struct("Xcm")
77 .field("kind", &self.kind)
78 .field("data", &self.data)
79 .finish()
80 }
81}
82
83#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
84pub enum XcmPayload {
85 Raw(Vec<u8>),
87 CreateAsset { token: H160, network: Network },
89}
90
91#[derive(Clone, Copy, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)]
93pub enum Network {
94 Polkadot,
96}
97
98#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
101pub struct Message {
102 pub gateway: H160,
104 pub nonce: u64,
106 pub origin: H160,
108 pub assets: Vec<EthereumAsset>,
110 pub xcm: XcmPayload,
112 pub claimer: Option<Vec<u8>>,
114 pub value: u128,
116 pub execution_fee: u128,
118 pub relayer_fee: u128,
120}
121
122#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
126pub enum EthereumAsset {
127 NativeTokenERC20 {
128 token_id: H160,
130 value: u128,
132 },
133 ForeignTokenERC20 {
134 token_id: H256,
136 value: u128,
138 },
139}
140
141#[derive(Copy, Clone, RuntimeDebug)]
142pub struct MessageDecodeError;
143
144impl TryFrom<&Log> for Message {
145 type Error = MessageDecodeError;
146
147 fn try_from(log: &Log) -> Result<Self, Self::Error> {
148 let topics: Vec<B256> = log.topics.iter().map(|x| B256::from_slice(x.as_ref())).collect();
150
151 let event = IGatewayV2::OutboundMessageAccepted::decode_raw_log_validate(topics, &log.data)
153 .map_err(|_| MessageDecodeError)?;
154
155 let payload = event.payload;
156
157 let substrate_assets = Self::extract_assets(&payload)?;
158
159 let xcm = XcmPayload::try_from(&payload)?;
160
161 let mut claimer = None;
162 if payload.claimer.len() > 0 {
163 claimer = Some(payload.claimer.to_vec());
164 }
165
166 let message = Message {
167 gateway: log.address,
168 nonce: event.nonce,
169 origin: H160::from(payload.origin.as_ref()),
170 assets: substrate_assets,
171 xcm,
172 claimer,
173 value: payload.value,
174 execution_fee: payload.executionFee,
175 relayer_fee: payload.relayerFee,
176 };
177
178 Ok(message)
179 }
180}
181
182impl Message {
183 fn extract_assets(
184 payload: &IGatewayV2::Payload,
185 ) -> Result<Vec<EthereumAsset>, MessageDecodeError> {
186 let mut substrate_assets = vec![];
187 for asset in &payload.assets {
188 substrate_assets.push(EthereumAsset::try_from(asset)?);
189 }
190 Ok(substrate_assets)
191 }
192}
193
194impl TryFrom<&IGatewayV2::Payload> for XcmPayload {
195 type Error = MessageDecodeError;
196
197 fn try_from(payload: &Payload) -> Result<Self, Self::Error> {
198 let xcm = match payload.xcm.kind {
199 0 => XcmPayload::Raw(payload.xcm.data.to_vec()),
200 1 => {
201 let create_asset =
202 IGatewayV2::XcmCreateAsset::abi_decode_validate(&payload.xcm.data)
203 .map_err(|_| MessageDecodeError)?;
204 let network = match create_asset.network {
206 0 => Network::Polkadot,
207 _ => return Err(MessageDecodeError),
208 };
209 XcmPayload::CreateAsset { token: H160::from(create_asset.token.as_ref()), network }
210 },
211 _ => return Err(MessageDecodeError),
212 };
213 Ok(xcm)
214 }
215}
216
217impl TryFrom<&IGatewayV2::EthereumAsset> for EthereumAsset {
218 type Error = MessageDecodeError;
219
220 fn try_from(asset: &IGatewayV2::EthereumAsset) -> Result<EthereumAsset, Self::Error> {
221 let asset = match asset.kind {
222 0 => {
223 let native_data = IGatewayV2::AsNativeTokenERC20::abi_decode_validate(&asset.data)
224 .map_err(|_| MessageDecodeError)?;
225 EthereumAsset::NativeTokenERC20 {
226 token_id: H160::from(native_data.token_id.as_ref()),
227 value: native_data.value,
228 }
229 },
230 1 => {
231 let foreign_data =
232 IGatewayV2::AsForeignTokenERC20::abi_decode_validate(&asset.data)
233 .map_err(|_| MessageDecodeError)?;
234 EthereumAsset::ForeignTokenERC20 {
235 token_id: H256::from(foreign_data.token_id.as_ref()),
236 value: foreign_data.value,
237 }
238 },
239 _ => return Err(MessageDecodeError),
240 };
241 Ok(asset)
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use frame_support::assert_ok;
249 use hex_literal::hex;
250 use sp_core::H160;
251
252 #[test]
253 fn test_decode() {
254 let log = Log{
255 address: hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305").into(),
256 topics: vec![hex!("550e2067494b1736ea5573f2d19cdc0ac95b410fff161bf16f11c6229655ec9c").into()],
257 data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(),
258 };
259
260 let result = Message::try_from(&log);
261 assert_ok!(result.clone());
262 let message = result.unwrap();
263
264 assert_eq!(H160::from(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305")), message.gateway);
265 assert_eq!(H160::from(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305")), message.origin);
266 }
267}