diff --git a/Cargo.toml b/Cargo.toml
index 3eb8697f..a469b907 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -127,6 +127,9 @@ xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.3
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
+orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.39", default-features = false }
+orml-xcm-support = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.39", default-features = false }
+orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.39", default-features = false }
# (native)
polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39" }
diff --git a/precompiles/utils/src/bytes.rs b/precompiles/utils/src/bytes.rs
new file mode 100644
index 00000000..047b97bf
--- /dev/null
+++ b/precompiles/utils/src/bytes.rs
@@ -0,0 +1,220 @@
+// This file is part of Astar.
+
+// Copyright 2019-2022 PureStake Inc.
+// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of Utils package, originally developed by Purestake Inc.
+// Utils package used in Astar Network in terms of GPLv3.
+//
+// Utils is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Utils is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Utils. If not, see .
+
+use super::*;
+use alloc::borrow::ToOwned;
+pub use alloc::string::String;
+use sp_core::{ConstU32, Get};
+
+type ConstU32Max = ConstU32<{ u32::MAX }>;
+
+pub type UnboundedBytes = BoundedBytesString;
+pub type BoundedBytes = BoundedBytesString;
+
+trait Kind {
+ fn signature() -> String;
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct BytesKind;
+
+impl Kind for BytesKind {
+ fn signature() -> String {
+ String::from("bytes")
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct StringKind;
+
+impl Kind for StringKind {
+ fn signature() -> String {
+ String::from("string")
+ }
+}
+
+/// The `bytes/string` type of Solidity.
+/// It is different from `Vec` which will be serialized with padding for each `u8` element
+/// of the array, while `Bytes` is tightly packed.
+#[derive(Debug)]
+pub struct BoundedBytesString {
+ data: Vec,
+ _phantom: PhantomData<(K, S)>,
+}
+
+impl> Clone for BoundedBytesString {
+ fn clone(&self) -> Self {
+ Self {
+ data: self.data.clone(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl PartialEq> for BoundedBytesString {
+ fn eq(&self, other: &BoundedBytesString) -> bool {
+ self.data.eq(&other.data)
+ }
+}
+
+impl Eq for BoundedBytesString {}
+
+impl> BoundedBytesString {
+ pub fn as_bytes(&self) -> &[u8] {
+ &self.data
+ }
+
+ pub fn as_str(&self) -> Result<&str, sp_std::str::Utf8Error> {
+ sp_std::str::from_utf8(&self.data)
+ }
+}
+
+impl> EvmData for BoundedBytesString {
+ fn read(reader: &mut EvmDataReader) -> EvmResult {
+ let mut inner_reader = reader.read_pointer()?;
+
+ // Read bytes/string size.
+ let array_size: usize = inner_reader
+ .read::()
+ .map_err(|_| revert("length, out of bounds"))?
+ .try_into()
+ .map_err(|_| revert("length, value too large"))?;
+
+ if array_size > S::get() as usize {
+ return Err(revert("length, value too large").into());
+ }
+
+ // Get valid range over the bytes data.
+ let range = inner_reader.move_cursor(array_size)?;
+
+ let data = inner_reader
+ .get_input_from_range(range)
+ .ok_or_else(|| revert(K::signature()))?;
+
+ let bytes = Self {
+ data: data.to_owned(),
+ _phantom: PhantomData,
+ };
+
+ Ok(bytes)
+ }
+
+ fn write(writer: &mut EvmDataWriter, value: Self) {
+ let value: Vec<_> = value.into();
+ let length = value.len();
+
+ // Pad the data.
+ // Leave it as is if a multiple of 32, otherwise pad to next
+ // multiple or 32.
+ let chunks = length / 32;
+ let padded_size = match length % 32 {
+ 0 => chunks * 32,
+ _ => (chunks + 1) * 32,
+ };
+
+ let mut value = value.to_vec();
+ value.resize(padded_size, 0);
+
+ writer.write_pointer(
+ EvmDataWriter::new()
+ .write(U256::from(length))
+ .write_raw_bytes(&value)
+ .build(),
+ );
+ }
+
+ fn has_static_size() -> bool {
+ false
+ }
+}
+
+// BytesString <=> Vec/&[u8]
+
+impl From> for Vec {
+ fn from(value: BoundedBytesString) -> Self {
+ value.data
+ }
+}
+
+impl From> for BoundedBytesString {
+ fn from(value: Vec) -> Self {
+ Self {
+ data: value,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<&[u8]> for BoundedBytesString {
+ fn from(value: &[u8]) -> Self {
+ Self {
+ data: value.to_vec(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<[u8; N]> for BoundedBytesString {
+ fn from(value: [u8; N]) -> Self {
+ Self {
+ data: value.to_vec(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<&[u8; N]> for BoundedBytesString {
+ fn from(value: &[u8; N]) -> Self {
+ Self {
+ data: value.to_vec(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+// BytesString <=> String/str
+
+impl TryFrom> for String {
+ type Error = alloc::string::FromUtf8Error;
+
+ fn try_from(value: BoundedBytesString) -> Result {
+ alloc::string::String::from_utf8(value.data)
+ }
+}
+
+impl From<&str> for BoundedBytesString {
+ fn from(value: &str) -> Self {
+ Self {
+ data: value.as_bytes().into(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From for BoundedBytesString {
+ fn from(value: String) -> Self {
+ Self {
+ data: value.as_bytes().into(),
+ _phantom: PhantomData,
+ }
+ }
+}
diff --git a/precompiles/utils/src/data.rs b/precompiles/utils/src/data.rs
index 2359bcef..75f32c91 100644
--- a/precompiles/utils/src/data.rs
+++ b/precompiles/utils/src/data.rs
@@ -23,9 +23,9 @@
use crate::{revert, EvmResult};
use alloc::borrow::ToOwned;
-use core::{any::type_name, ops::Range};
+use core::{any::type_name, marker::PhantomData, ops::Range};
use impl_trait_for_tuples::impl_for_tuples;
-use sp_core::{H160, H256, U256};
+use sp_core::{Get, H160, H256, U256};
use sp_std::{convert::TryInto, vec, vec::Vec};
/// The `address` type of Solidity.
@@ -175,6 +175,11 @@ impl<'a> EvmDataReader<'a> {
})
}
+ /// Return Option<&[u8]> from a given range for EvmDataReader
+ pub fn get_input_from_range(&self, range: Range) -> Option<&[u8]> {
+ self.input.get(range)
+ }
+
/// Read remaining bytes
pub fn read_till_end(&mut self) -> EvmResult<&[u8]> {
let range = self.move_cursor(self.input.len() - self.cursor)?;
@@ -190,7 +195,7 @@ impl<'a> EvmDataReader<'a> {
/// Move the reading cursor with provided length, and return a range from the previous cursor
/// location to the new one.
/// Checks cursor overflows.
- fn move_cursor(&mut self, len: usize) -> EvmResult> {
+ pub fn move_cursor(&mut self, len: usize) -> EvmResult> {
let start = self.cursor;
let end = self
.cursor
@@ -285,7 +290,7 @@ impl EvmDataWriter {
/// Write arbitrary bytes.
/// Doesn't handle any alignement checks, prefer using `write` instead if possible.
- fn write_raw_bytes(mut self, value: &[u8]) -> Self {
+ pub fn write_raw_bytes(mut self, value: &[u8]) -> Self {
self.data.extend_from_slice(value);
self
}
@@ -604,3 +609,104 @@ impl EvmData for Bytes {
false
}
}
+/// Wrapper around a Vec that provides a max length bound on read.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct BoundedVec {
+ inner: Vec,
+ _phantom: PhantomData,
+}
+
+impl> EvmData for BoundedVec {
+ fn read(reader: &mut EvmDataReader) -> EvmResult {
+ let mut inner_reader = reader.read_pointer()?;
+
+ let array_size: usize = inner_reader
+ .read::()
+ .map_err(|_| revert("out of bounds: length of array"))?
+ .try_into()
+ .map_err(|_| revert("value too large : Array has more than max items allowed"))?;
+
+ if array_size > S::get() as usize {
+ return Err(revert("value too large : Array has more than max items allowed").into());
+ }
+
+ let mut array = vec![];
+
+ let mut item_reader = EvmDataReader {
+ input: inner_reader
+ .input
+ .get(32..)
+ .ok_or_else(|| revert("read out of bounds: array content"))?,
+ cursor: 0,
+ };
+
+ for _ in 0..array_size {
+ array.push(item_reader.read()?);
+ }
+
+ Ok(BoundedVec {
+ inner: array,
+ _phantom: PhantomData,
+ })
+ }
+
+ fn write(writer: &mut EvmDataWriter, value: Self) {
+ let value: Vec<_> = value.into();
+ let mut inner_writer = EvmDataWriter::new().write(U256::from(value.len()));
+
+ for inner in value {
+ // Any offset in items are relative to the start of the item instead of the
+ // start of the array. However if there is offseted data it must but appended after
+ // all items (offsets) are written. We thus need to rely on `compute_offsets` to do
+ // that, and must store a "shift" to correct the offsets.
+ let shift = inner_writer.data.len();
+ let item_writer = EvmDataWriter::new().write(inner);
+
+ inner_writer = inner_writer.write_raw_bytes(&item_writer.data);
+ for mut offset_datum in item_writer.offset_data {
+ offset_datum.offset_shift += 32;
+ offset_datum.offset_position += shift;
+ inner_writer.offset_data.push(offset_datum);
+ }
+ }
+
+ writer.write_pointer(inner_writer.build());
+ }
+
+ fn has_static_size() -> bool {
+ false
+ }
+}
+
+impl From> for BoundedVec {
+ fn from(value: Vec) -> Self {
+ BoundedVec {
+ inner: value,
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<&[T]> for BoundedVec {
+ fn from(value: &[T]) -> Self {
+ BoundedVec {
+ inner: value.to_vec(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From<[T; N]> for BoundedVec {
+ fn from(value: [T; N]) -> Self {
+ BoundedVec {
+ inner: value.to_vec(),
+ _phantom: PhantomData,
+ }
+ }
+}
+
+impl From> for Vec {
+ fn from(value: BoundedVec) -> Self {
+ value.inner
+ }
+}
diff --git a/precompiles/utils/src/lib.rs b/precompiles/utils/src/lib.rs
index f36412a2..ab27e200 100644
--- a/precompiles/utils/src/lib.rs
+++ b/precompiles/utils/src/lib.rs
@@ -37,7 +37,9 @@ use pallet_evm::{GasWeightMapping, Log};
use sp_core::{H160, H256, U256};
use sp_std::{marker::PhantomData, vec, vec::Vec};
-mod data;
+pub mod bytes;
+pub mod data;
+pub mod xcm;
pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter};
pub use precompile_utils_macro::{generate_function_selector, keccak256};
diff --git a/precompiles/utils/src/tests.rs b/precompiles/utils/src/tests.rs
index 6756687b..57938fac 100644
--- a/precompiles/utils/src/tests.rs
+++ b/precompiles/utils/src/tests.rs
@@ -20,9 +20,15 @@
// You should have received a copy of the GNU General Public License
// along with Utils. If not, see .
+use crate::bytes::UnboundedBytes;
+
use super::*;
use hex_literal::hex;
use sp_core::{H256, U256};
+use {
+ crate::xcm::{network_id_from_bytes, network_id_to_bytes},
+ ::xcm::latest::{Junction, Junctions, NetworkId},
+};
fn u256_repeat_byte(byte: u8) -> U256 {
let value = H256::repeat_byte(byte);
@@ -506,6 +512,19 @@ fn read_bytes() {
assert_eq!(data, parsed.as_bytes());
}
+#[test]
+fn read_unbounded_bytes() {
+ let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\
+ tempor incididunt ut labore et dolore magna aliqua.";
+ let writer_output = EvmDataWriter::new()
+ .write(UnboundedBytes::from(&data[..]))
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: UnboundedBytes = reader.read().expect("to correctly parse Bytes");
+
+ assert_eq!(data, parsed.as_bytes());
+}
#[test]
fn write_bytes() {
let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\
@@ -529,6 +548,31 @@ fn write_bytes() {
assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60]));
assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80]));
}
+#[test]
+fn write_unbounded_bytes() {
+ let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\
+ tempor incididunt ut labore et dolore magna aliqua.";
+
+ let writer_output = EvmDataWriter::new()
+ .write(UnboundedBytes::from(&data[..]))
+ .build();
+
+ // We can read this "manualy" using simpler functions.
+ let mut reader = EvmDataReader::new(&writer_output);
+
+ // We pad data to a multiple of 32 bytes.
+ let mut padded = data.to_vec();
+ assert!(data.len() < 0x80);
+ padded.resize(0x80, 0);
+
+ assert_eq!(reader.read::().expect("read offset"), 32.into());
+ assert_eq!(reader.read::().expect("read size"), data.len().into());
+ let mut read = |e| reader.read::().expect(e); // shorthand
+ assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20]));
+ assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40]));
+ assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60]));
+ assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80]));
+}
#[test]
fn read_string() {
@@ -618,7 +662,61 @@ fn write_vec_bytes() {
assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60]));
assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80]));
}
+#[test]
+fn write_vec_unbounded_bytes() {
+ let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\
+ tempor incididunt ut labore et dolore magna aliqua.";
+ let writer_output = EvmDataWriter::new()
+ .write(vec![
+ UnboundedBytes::from(&data[..]),
+ UnboundedBytes::from(&data[..]),
+ ])
+ .build();
+
+ writer_output
+ .chunks_exact(32)
+ .map(|chunk| H256::from_slice(chunk))
+ .for_each(|hash| println!("{:?}", hash));
+
+ // We pad data to a multiple of 32 bytes.
+ let mut padded = data.to_vec();
+ assert!(data.len() < 0x80);
+ padded.resize(0x80, 0);
+
+ let mut reader = EvmDataReader::new(&writer_output);
+
+ // Offset of vec
+ assert_eq!(reader.read::().expect("read offset"), 32.into());
+
+ // Length of vec
+ assert_eq!(reader.read::().expect("read offset"), 2.into());
+
+ // Relative offset of first bytgmes object
+ assert_eq!(reader.read::().expect("read offset"), 0x40.into());
+ // Relative offset of second bytes object
+ assert_eq!(reader.read::().expect("read offset"), 0xe0.into());
+
+ // Length of first bytes object
+ assert_eq!(reader.read::().expect("read size"), data.len().into());
+
+ // First byte objects data
+ let mut read = |e| reader.read::().expect(e); // shorthand
+ assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20]));
+ assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40]));
+ assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60]));
+ assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80]));
+
+ // Length of second bytes object
+ assert_eq!(reader.read::().expect("read size"), data.len().into());
+
+ // Second byte objects data
+ let mut read = |e| reader.read::().expect(e); // shorthand
+ assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20]));
+ assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40]));
+ assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60]));
+ assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80]));
+}
#[test]
fn read_vec_of_bytes() {
let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\
@@ -744,3 +842,167 @@ fn read_complex_solidity_function() {
// weight
assert_eq!(reader.read::().unwrap(), 100u32.into());
}
+
+#[test]
+fn read_dynamic_size_tuple() {
+ // (uint8, bytes[]) encoded by web3
+ let data = hex!(
+ "0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000001
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000001
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000001
+ 0100000000000000000000000000000000000000000000000000000000000000"
+ );
+
+ let mut reader = EvmDataReader::new(&data);
+
+ assert_eq!(
+ reader.read::<(u8, Vec)>().unwrap(),
+ (1, vec![UnboundedBytes::from(vec![0x01])])
+ );
+}
+#[test]
+fn junctions_decoder_works() {
+ let writer_output = EvmDataWriter::new()
+ .write(Junctions::X1(Junction::OnlyChild))
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junctions = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(parsed, Junctions::X1(Junction::OnlyChild));
+
+ let writer_output = EvmDataWriter::new()
+ .write(Junctions::X2(Junction::OnlyChild, Junction::OnlyChild))
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junctions = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(
+ parsed,
+ Junctions::X2(Junction::OnlyChild, Junction::OnlyChild)
+ );
+
+ let writer_output = EvmDataWriter::new()
+ .write(Junctions::X3(
+ Junction::OnlyChild,
+ Junction::OnlyChild,
+ Junction::OnlyChild,
+ ))
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junctions = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(
+ parsed,
+ Junctions::X3(
+ Junction::OnlyChild,
+ Junction::OnlyChild,
+ Junction::OnlyChild
+ ),
+ );
+}
+
+#[test]
+fn junction_decoder_works() {
+ let writer_output = EvmDataWriter::new().write(Junction::Parachain(0)).build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junction = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(parsed, Junction::Parachain(0));
+
+ let writer_output = EvmDataWriter::new()
+ .write(Junction::AccountId32 {
+ network: None,
+ id: [1u8; 32],
+ })
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junction = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(
+ parsed,
+ Junction::AccountId32 {
+ network: None,
+ id: [1u8; 32],
+ }
+ );
+
+ let writer_output = EvmDataWriter::new()
+ .write(Junction::AccountIndex64 {
+ network: None,
+ index: u64::from_be_bytes([1u8; 8]),
+ })
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junction = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(
+ parsed,
+ Junction::AccountIndex64 {
+ network: None,
+ index: u64::from_be_bytes([1u8; 8]),
+ }
+ );
+
+ let writer_output = EvmDataWriter::new()
+ .write(Junction::AccountKey20 {
+ network: None,
+ key: H160::repeat_byte(0xAA).as_bytes().try_into().unwrap(),
+ })
+ .build();
+
+ let mut reader = EvmDataReader::new(&writer_output);
+ let parsed: Junction = reader
+ .read::()
+ .expect("to correctly parse Junctions");
+
+ assert_eq!(
+ parsed,
+ Junction::AccountKey20 {
+ network: None,
+ key: H160::repeat_byte(0xAA).as_bytes().try_into().unwrap(),
+ }
+ );
+}
+
+#[test]
+fn network_id_decoder_works() {
+ assert_eq!(network_id_from_bytes(network_id_to_bytes(None)), Ok(None));
+
+ let mut name = [0u8; 32];
+ name[0..6].copy_from_slice(b"myname");
+ assert_eq!(
+ network_id_from_bytes(network_id_to_bytes(Some(NetworkId::ByGenesis(name)))),
+ Ok(Some(NetworkId::ByGenesis(name)))
+ );
+
+ assert_eq!(
+ network_id_from_bytes(network_id_to_bytes(Some(NetworkId::Kusama))),
+ Ok(Some(NetworkId::Kusama))
+ );
+
+ assert_eq!(
+ network_id_from_bytes(network_id_to_bytes(Some(NetworkId::Polkadot))),
+ Ok(Some(NetworkId::Polkadot))
+ );
+}
diff --git a/precompiles/utils/src/xcm.rs b/precompiles/utils/src/xcm.rs
new file mode 100644
index 00000000..96942a4b
--- /dev/null
+++ b/precompiles/utils/src/xcm.rs
@@ -0,0 +1,428 @@
+// This file is part of Astar.
+
+// Copyright 2019-2022 PureStake Inc.
+// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of Utils package, originally developed by Purestake Inc.
+// Utils package used in Astar Network in terms of GPLv3.
+//
+// Utils is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Utils is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Utils. If not, see .
+
+//! Encoding of XCM types for solidity
+
+use crate::Address;
+use sp_core::U256;
+use {
+ crate::{bytes::*, revert, EvmData, EvmDataReader, EvmDataWriter, EvmResult},
+ frame_support::{ensure, traits::ConstU32},
+ sp_core::H256,
+ sp_std::vec::Vec,
+ xcm::latest::{Junction, Junctions, MultiLocation, NetworkId},
+};
+pub const JUNCTION_SIZE_LIMIT: u32 = 2u32.pow(16);
+
+// Function to convert network id to bytes
+// Each NetworkId variant is represented as bytes
+// The first byte represents the enum variant to be used.
+// - Indexes 0,2,3 represent XCM V2 variants
+// - Index 1 changes name in V3 (`ByGenesis`), but is compatible with V2 `Named`
+// - Indexes 4~10 represent new XCM V3 variants
+// The rest of the bytes (if any), represent the additional data that such enum variant requires
+// In such a case, since NetworkIds will be appended at the end, we will read the buffer until the
+// end to recover the name
+
+pub(crate) fn network_id_to_bytes(network_id: Option) -> Vec {
+ let mut encoded: Vec = Vec::new();
+ match network_id.clone() {
+ None => {
+ encoded.push(0u8);
+ encoded
+ }
+ Some(NetworkId::ByGenesis(id)) => {
+ encoded.push(1u8);
+ encoded.append(&mut id.into());
+ encoded
+ }
+ Some(NetworkId::Polkadot) => {
+ encoded.push(2u8);
+ encoded.push(2u8);
+ encoded
+ }
+ Some(NetworkId::Kusama) => {
+ encoded.push(3u8);
+ encoded.push(3u8);
+ encoded
+ }
+ Some(NetworkId::ByFork {
+ block_number,
+ block_hash,
+ }) => {
+ encoded.push(4u8);
+ encoded.push(1u8);
+ encoded.append(&mut block_number.to_be_bytes().into());
+ encoded.append(&mut block_hash.into());
+ encoded
+ }
+ Some(NetworkId::Westend) => {
+ encoded.push(5u8);
+ encoded.push(4u8);
+ encoded
+ }
+ Some(NetworkId::Rococo) => {
+ encoded.push(6u8);
+ encoded.push(5u8);
+ encoded
+ }
+ Some(NetworkId::Wococo) => {
+ encoded.push(7u8);
+ encoded.push(6u8);
+ encoded
+ }
+ Some(NetworkId::Ethereum { chain_id }) => {
+ encoded.push(8u8);
+ encoded.push(7u8);
+ encoded.append(&mut chain_id.to_be_bytes().into());
+ encoded
+ }
+ Some(NetworkId::BitcoinCore) => {
+ encoded.push(9u8);
+ encoded.push(8u8);
+ encoded
+ }
+ Some(NetworkId::BitcoinCash) => {
+ encoded.push(10u8);
+ encoded.push(9u8);
+ encoded
+ }
+ }
+}
+
+// Function to convert bytes to networkId
+pub(crate) fn network_id_from_bytes(encoded_bytes: Vec) -> EvmResult