multiversx_sc/types/interaction/expr/
test_sc_address.rs

1use core::ptr;
2
3use multiversx_sc_codec::{
4    EncodeErrorHandler, NestedEncode, NestedEncodeOutput, TopEncode, TopEncodeOutput,
5};
6
7use crate::{
8    abi::TypeAbiFrom,
9    api::ManagedTypeApi,
10    types::{
11        heap::Address, AnnotatedValue, ManagedAddress, ManagedBuffer, TxEnv, TxFrom,
12        TxFromSpecified, TxTo, TxToSpecified,
13    },
14};
15
16use super::TestAddress;
17
18const SC_PREFIX: &str = "sc:";
19const VM_TYPE_LEN: usize = 2;
20const DEFAULT_VM_TYPE: &[u8] = &[5, 0];
21
22/// Encodes a dummy SC address, to be used for tests.
23///
24/// It is designed to be usable from contracts (especiall test contracts), with a minimal footprint.
25/// For this reason, its inner structure is subject to change.
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct TestSCAddress<'a> {
28    name: &'a str,
29}
30
31impl<'a> TestSCAddress<'a> {
32    pub const fn new(name: &'a str) -> Self {
33        TestSCAddress { name }
34    }
35}
36
37impl<Env> AnnotatedValue<Env, ManagedAddress<Env::Api>> for TestSCAddress<'_>
38where
39    Env: TxEnv,
40{
41    fn annotation(&self, _env: &Env) -> ManagedBuffer<Env::Api> {
42        let mut result = ManagedBuffer::new_from_bytes(SC_PREFIX.as_bytes());
43        result.append_bytes(self.name.as_bytes());
44        result
45    }
46
47    fn to_value(&self, _env: &Env) -> ManagedAddress<Env::Api> {
48        let expr: [u8; 32] = self.eval_to_array();
49        expr.into()
50    }
51}
52
53impl TestSCAddress<'_> {
54    pub fn to_address(&self) -> Address {
55        self.eval_to_array().into()
56    }
57
58    pub fn to_managed_address<Api: ManagedTypeApi>(&self) -> ManagedAddress<Api> {
59        self.eval_to_array().into()
60    }
61}
62
63impl PartialEq<TestAddress<'_>> for TestSCAddress<'_> {
64    fn eq(&self, other: &TestAddress) -> bool {
65        self.to_address() == other.to_address()
66    }
67}
68
69impl PartialEq<Address> for TestSCAddress<'_> {
70    fn eq(&self, other: &Address) -> bool {
71        &self.to_address() == other
72    }
73}
74
75impl<'a> PartialEq<TestSCAddress<'a>> for Address {
76    fn eq(&self, other: &TestSCAddress<'a>) -> bool {
77        self == &other.to_address()
78    }
79}
80
81#[cfg(feature = "std")]
82impl PartialEq<multiversx_chain_core::std::Bech32Address> for TestSCAddress<'_> {
83    fn eq(&self, other: &multiversx_chain_core::std::Bech32Address) -> bool {
84        self.to_address() == other.address
85    }
86}
87
88#[cfg(feature = "std")]
89impl<'a> PartialEq<TestSCAddress<'a>> for multiversx_chain_core::std::Bech32Address {
90    fn eq(&self, other: &TestSCAddress<'a>) -> bool {
91        self.address == other.to_address()
92    }
93}
94
95impl<Api: ManagedTypeApi> PartialEq<ManagedAddress<Api>> for TestSCAddress<'_> {
96    fn eq(&self, other: &ManagedAddress<Api>) -> bool {
97        self.to_address() == other.to_address()
98    }
99}
100
101impl<'a, Api: ManagedTypeApi> PartialEq<TestSCAddress<'a>> for ManagedAddress<Api> {
102    fn eq(&self, other: &TestSCAddress<'a>) -> bool {
103        self.to_address() == other.to_address()
104    }
105}
106
107impl<Env> TxFrom<Env> for TestSCAddress<'_>
108where
109    Env: TxEnv,
110{
111    fn resolve_address(&self, _env: &Env) -> ManagedAddress<Env::Api> {
112        let expr: [u8; 32] = self.eval_to_array();
113        expr.into()
114    }
115}
116impl<Env> TxFromSpecified<Env> for TestSCAddress<'_> where Env: TxEnv {}
117impl<Env> TxTo<Env> for TestSCAddress<'_> where Env: TxEnv {}
118impl<Env> TxToSpecified<Env> for TestSCAddress<'_> where Env: TxEnv {}
119
120impl TestSCAddress<'_> {
121    pub fn eval_to_array(&self) -> [u8; 32] {
122        let result = *b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00______________________";
123        let expr_bytes = self.name.as_bytes();
124        let mut len = expr_bytes.len();
125        if len > 22 {
126            len = 22;
127        }
128        unsafe {
129            ptr::copy_nonoverlapping(
130                DEFAULT_VM_TYPE.as_ptr(),
131                result.as_ptr().offset(8) as *mut u8,
132                VM_TYPE_LEN,
133            );
134            ptr::copy_nonoverlapping(
135                expr_bytes.as_ptr(),
136                result.as_ptr().offset(10) as *mut u8,
137                len,
138            );
139        }
140        result
141    }
142
143    #[cfg(feature = "alloc")]
144    pub fn eval_to_expr(&self) -> alloc::string::String {
145        alloc::format!("{SC_PREFIX}{}", self.name)
146    }
147}
148
149impl TopEncode for TestSCAddress<'_> {
150    fn top_encode_or_handle_err<O, H>(&self, output: O, h: H) -> Result<(), H::HandledErr>
151    where
152        O: TopEncodeOutput,
153        H: EncodeErrorHandler,
154    {
155        self.eval_to_array().top_encode_or_handle_err(output, h)
156    }
157}
158
159impl NestedEncode for TestSCAddress<'_> {
160    #[inline]
161    fn dep_encode_or_handle_err<O, H>(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr>
162    where
163        O: NestedEncodeOutput,
164        H: EncodeErrorHandler,
165    {
166        self.eval_to_array().dep_encode_or_handle_err(dest, h)
167    }
168}
169
170impl<Api> TypeAbiFrom<TestSCAddress<'_>> for ManagedAddress<Api> where Api: ManagedTypeApi {}
171
172#[cfg(test)]
173pub mod tests {
174    use super::*;
175
176    fn assert_eq_eval(expr: &'static str, expected: &[u8; 32]) {
177        assert_eq!(&TestSCAddress::new(expr).eval_to_array(), expected);
178    }
179
180    #[test]
181    fn test_address_value() {
182        assert_eq_eval(
183            "",
184            b"\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00______________________",
185        );
186        assert_eq_eval(
187            "a",
188            b"\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00a_____________________",
189        );
190        assert_eq_eval(
191            "12345678901234567890120s",
192            b"\x00\x00\x00\x00\x00\x00\x00\x00\x05\x001234567890123456789012",
193        );
194    }
195}