Skip to main content

multiversx_sc/hex_call_data/
cd_ser.rs

1use crate::{
2    api::ManagedTypeApi,
3    formatter::hex_util::byte_to_hex_digits,
4    types::{ManagedArgBuffer, ManagedBuffer, heap::ArgBuffer},
5};
6use alloc::vec::Vec;
7
8use super::SEPARATOR;
9
10/// Serializes to the MultiversX smart contract call format.
11///
12/// This format consists of the function name, followed by '@', followed by hex-encoded argument bytes separated by '@' characters.
13/// Example: "funcName@00000@aaaa@1234@@".
14/// Arguments can be empty, in which case no hex digits are emitted.
15/// Argument hex encodings will always have an even number of digits.
16///
17/// HexCallDataSerializer owns its output.
18///
19/// Converting from whatever type the argument to bytes is not in scope. Use the `serializer` module for that.
20///
21pub struct HexCallDataSerializer(Vec<u8>);
22
23impl HexCallDataSerializer {
24    pub fn new(endpoint_name: &[u8]) -> Self {
25        let mut data = Vec::with_capacity(endpoint_name.len());
26        data.extend_from_slice(endpoint_name);
27        HexCallDataSerializer(data)
28    }
29
30    pub fn from_arg_buffer(endpoint_name: &[u8], arg_buffer: &ArgBuffer) -> Self {
31        let mut hex_data = HexCallDataSerializer::new(endpoint_name);
32        arg_buffer.for_each_arg(|arg_bytes| hex_data.push_argument_bytes(arg_bytes));
33        hex_data
34    }
35
36    pub fn from_managed_arg_buffer<M: ManagedTypeApi>(
37        endpoint_name: &ManagedBuffer<M>,
38        arg_buffer: &ManagedArgBuffer<M>,
39    ) -> Self {
40        let mut hex_data = HexCallDataSerializer::new(endpoint_name.to_boxed_bytes().as_slice());
41        for arg in arg_buffer.raw_arg_iter() {
42            hex_data.push_argument_bytes(arg.to_boxed_bytes().as_slice());
43        }
44        hex_data
45    }
46
47    pub fn as_slice(&self) -> &[u8] {
48        self.0.as_slice()
49    }
50
51    pub fn into_vec(self) -> Vec<u8> {
52        self.0
53    }
54
55    fn push_byte(&mut self, byte: u8) {
56        let digits = byte_to_hex_digits(byte);
57        self.0.push(digits[0]);
58        self.0.push(digits[1]);
59    }
60
61    pub fn push_argument_bytes(&mut self, bytes: &[u8]) {
62        self.0.reserve(1 + bytes.len() * 2);
63        self.0.push(SEPARATOR);
64        for byte in bytes.iter() {
65            self.push_byte(*byte);
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn test_push_bytes_1() {
76        let mut cd = HexCallDataSerializer::new(b"func");
77        let arg_bytes: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
78        cd.push_argument_bytes(arg_bytes);
79        assert_eq!(cd.as_slice(), &b"func@0123456789abcdef"[..]);
80    }
81
82    #[test]
83    fn test_push_bytes_2() {
84        let mut cd = HexCallDataSerializer::new(b"func");
85        let arg_bytes: &[u8] = &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
86        cd.push_argument_bytes(arg_bytes);
87        cd.push_argument_bytes(arg_bytes);
88        assert_eq!(
89            cd.as_slice(),
90            &b"func@0123456789abcdef@0123456789abcdef"[..]
91        );
92    }
93
94    #[test]
95    fn test_push_empty_1() {
96        let mut cd = HexCallDataSerializer::new(b"func");
97        cd.push_argument_bytes(&[][..]);
98        assert_eq!(cd.as_slice(), &b"func@"[..]);
99    }
100
101    #[test]
102    fn test_push_empty_2() {
103        let mut cd = HexCallDataSerializer::new(b"func");
104        cd.push_argument_bytes(&[][..]);
105        cd.push_argument_bytes(&[][..]);
106        assert_eq!(cd.as_slice(), &b"func@@"[..]);
107    }
108
109    #[test]
110    fn test_push_empty_3() {
111        let mut cd = HexCallDataSerializer::new(b"");
112        cd.push_argument_bytes(&[][..]);
113        cd.push_argument_bytes(&[][..]);
114        cd.push_argument_bytes(&[][..]);
115        assert_eq!(cd.as_slice(), &b"@@@"[..]);
116    }
117
118    #[test]
119    fn test_push_some_empty_1() {
120        let mut cd = HexCallDataSerializer::new(b"func");
121        let arg_bytes: &[u8] = &[0xff, 0xff];
122        cd.push_argument_bytes(arg_bytes);
123        cd.push_argument_bytes(&[][..]);
124        assert_eq!(cd.as_slice(), &b"func@ffff@"[..]);
125    }
126
127    #[test]
128    fn test_push_some_empty_2() {
129        let mut cd = HexCallDataSerializer::new(b"func");
130        let arg_bytes: &[u8] = &[0xff, 0xff];
131        cd.push_argument_bytes(&[][..]);
132        cd.push_argument_bytes(&[][..]);
133        cd.push_argument_bytes(arg_bytes);
134        cd.push_argument_bytes(&[][..]);
135        cd.push_argument_bytes(&[][..]);
136        assert_eq!(cd.as_slice(), &b"func@@@ffff@@"[..]);
137    }
138}