soroban_rs/
scval.rs

1use std::time::Duration;
2
3use crate::SorobanHelperError;
4use stellar_xdr::curr::{
5    AccountId, BytesM, Duration as XDRDuration, ScAddress, ScBytes, ScString, ScVal, ScVec,
6    StringM, VecM,
7};
8
9/// A trait for converting native rust values into a `ScVal`.
10pub trait IntoScVal {
11    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError>;
12    fn into_val(self) -> ScVal;
13}
14
15/// Converts a Stellar `AccountId` into an `ScVal::Address` containing an account.
16impl IntoScVal for AccountId {
17    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
18        Ok(ScVal::Address(ScAddress::Account(self.clone())))
19    }
20
21    fn into_val(self) -> ScVal {
22        ScVal::Address(ScAddress::Account(self))
23    }
24}
25
26/// Converts a 32-bit unsigned integer into an `ScVal::U32`.
27impl IntoScVal for u32 {
28    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
29        Ok(ScVal::U32(*self))
30    }
31
32    fn into_val(self) -> ScVal {
33        ScVal::U32(self)
34    }
35}
36
37/// Converts a 64-bit unsigned integer into an `ScVal::U64`.
38impl IntoScVal for u64 {
39    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
40        Ok(ScVal::U64(*self))
41    }
42
43    fn into_val(self) -> ScVal {
44        ScVal::U64(self)
45    }
46}
47
48/// Converts a 32-bit signed integer into an `ScVal::I32`.
49impl IntoScVal for i32 {
50    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
51        Ok(ScVal::I32(*self))
52    }
53
54    fn into_val(self) -> ScVal {
55        ScVal::I32(self)
56    }
57}
58
59/// Converts a 64-bit signed integer into an `ScVal::I64`.
60impl IntoScVal for i64 {
61    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
62        Ok(ScVal::I64(*self))
63    }
64
65    fn into_val(self) -> ScVal {
66        ScVal::I64(self)
67    }
68}
69
70/// Converts a boolean value into an `ScVal::Bool`.
71impl IntoScVal for bool {
72    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
73        Ok(ScVal::Bool(*self))
74    }
75
76    fn into_val(self) -> ScVal {
77        ScVal::Bool(self)
78    }
79}
80
81/// Converts a Rust `String` into an `ScVal::String` by first converting to a Stellar `StringM`.
82impl IntoScVal for String {
83    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
84        let string_m = StringM::<{ u32::MAX }>::try_from(self).map_err(|_| {
85            SorobanHelperError::XdrEncodingFailed("Failed to convert String to StringM".to_string())
86        })?;
87        Ok(ScVal::String(ScString::from(string_m)))
88    }
89
90    fn into_val(self) -> ScVal {
91        let string_m =
92            StringM::<{ u32::MAX }>::try_from(self).expect("Failed to convert String to StringM");
93        ScVal::String(ScString::from(string_m))
94    }
95}
96
97/// Converts a 32-byte array into an `ScVal::Bytes`.
98impl IntoScVal for [u8; 32] {
99    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
100        let bytes_m = BytesM::<{ u32::MAX }>::try_from(self).map_err(|_| {
101            SorobanHelperError::XdrEncodingFailed("Failed to convert Bytes to BytesM".to_string())
102        })?;
103        Ok(ScVal::Bytes(ScBytes::from(bytes_m)))
104    }
105
106    fn into_val(self) -> ScVal {
107        let bytes_m =
108            BytesM::<{ u32::MAX }>::try_from(self).expect("Failed to convert Bytes to BytesM");
109        ScVal::Bytes(ScBytes::from(bytes_m))
110    }
111}
112
113/// Converts a Rust `Duration` into an `ScVal::Duration` using seconds as the time unit.
114impl IntoScVal for Duration {
115    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
116        let milis: u64 = self.as_secs();
117        Ok(ScVal::Duration(XDRDuration::from(milis)))
118    }
119
120    fn into_val(self) -> ScVal {
121        let milis: u64 = self.as_secs();
122        ScVal::Duration(XDRDuration::from(milis))
123    }
124}
125
126/// Converts a vector of `ScVal` objects into an `ScVal::Vec`.
127impl IntoScVal for Vec<ScVal> {
128    fn try_into_val(&self) -> Result<ScVal, SorobanHelperError> {
129        let vec_m = VecM::try_from(self).map_err(|_| {
130            SorobanHelperError::XdrEncodingFailed("Failed to convert Vec to VecM".to_string())
131        })?;
132        Ok(ScVal::Vec(Some(ScVec::from(vec_m))))
133    }
134
135    fn into_val(self) -> ScVal {
136        let vec_m = VecM::try_from(self).expect("Failed to convert Vec to VecM");
137        ScVal::Vec(Some(ScVec::from(vec_m)))
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use stellar_xdr::curr::{PublicKey, Uint256};
145
146    #[test]
147    fn test_account_id_into_scval() {
148        let public_key = PublicKey::PublicKeyTypeEd25519(Uint256([0; 32]));
149        let account_id = AccountId(public_key);
150
151        // Test try_into_val
152        let scval = account_id.try_into_val().unwrap();
153        match scval {
154            ScVal::Address(ScAddress::Account(id)) => {
155                assert_eq!(id, account_id);
156            }
157            _ => panic!(
158                "Expected ScVal::Address(ScAddress::Account), got {:?}",
159                scval
160            ),
161        }
162
163        // Test into_val
164        let scval = account_id.clone().into_val();
165        match scval {
166            ScVal::Address(ScAddress::Account(id)) => {
167                assert_eq!(id, account_id);
168            }
169            _ => panic!(
170                "Expected ScVal::Address(ScAddress::Account), got {:?}",
171                scval
172            ),
173        }
174    }
175
176    #[test]
177    fn test_u32_into_scval() {
178        let value: u32 = 42;
179
180        // Test try_into_val
181        let scval = value.try_into_val().unwrap();
182        match scval {
183            ScVal::U32(val) => {
184                assert_eq!(val, value);
185            }
186            _ => panic!("Expected ScVal::U32, got {:?}", scval),
187        }
188
189        // Test into_val
190        let scval = value.into_val();
191        match scval {
192            ScVal::U32(val) => {
193                assert_eq!(val, value);
194            }
195            _ => panic!("Expected ScVal::U32, got {:?}", scval),
196        }
197    }
198
199    #[test]
200    fn test_u64_into_scval() {
201        let value: u64 = 42;
202
203        // Test try_into_val
204        let scval = value.try_into_val().unwrap();
205        match scval {
206            ScVal::U64(val) => {
207                assert_eq!(val, value);
208            }
209            _ => panic!("Expected ScVal::U64, got {:?}", scval),
210        }
211
212        // Test into_val
213        let scval = value.into_val();
214        match scval {
215            ScVal::U64(val) => {
216                assert_eq!(val, value);
217            }
218            _ => panic!("Expected ScVal::U64, got {:?}", scval),
219        }
220    }
221
222    #[test]
223    fn test_i32_into_scval() {
224        let value: i32 = -42;
225
226        // Test try_into_val
227        let scval = value.try_into_val().unwrap();
228        match scval {
229            ScVal::I32(val) => {
230                assert_eq!(val, value);
231            }
232            _ => panic!("Expected ScVal::I32, got {:?}", scval),
233        }
234
235        // Test into_val
236        let scval = value.into_val();
237        match scval {
238            ScVal::I32(val) => {
239                assert_eq!(val, value);
240            }
241            _ => panic!("Expected ScVal::I32, got {:?}", scval),
242        }
243    }
244
245    #[test]
246    fn test_i64_into_scval() {
247        let value: i64 = -42;
248
249        // Test try_into_val
250        let scval = value.try_into_val().unwrap();
251        match scval {
252            ScVal::I64(val) => {
253                assert_eq!(val, value);
254            }
255            _ => panic!("Expected ScVal::I64, got {:?}", scval),
256        }
257
258        // Test into_val
259        let scval = value.into_val();
260        match scval {
261            ScVal::I64(val) => {
262                assert_eq!(val, value);
263            }
264            _ => panic!("Expected ScVal::I64, got {:?}", scval),
265        }
266    }
267
268    #[test]
269    fn test_bool_into_scval() {
270        // Test true value
271        let value = true;
272
273        let scval = value.try_into_val().unwrap();
274        match scval {
275            ScVal::Bool(val) => {
276                assert_eq!(val, value);
277            }
278            _ => panic!("Expected ScVal::Bool, got {:?}", scval),
279        }
280
281        let scval = value.into_val();
282        match scval {
283            ScVal::Bool(val) => {
284                assert_eq!(val, value);
285            }
286            _ => panic!("Expected ScVal::Bool, got {:?}", scval),
287        }
288
289        // Test false value
290        let value = false;
291
292        let scval = value.try_into_val().unwrap();
293        match scval {
294            ScVal::Bool(val) => {
295                assert_eq!(val, value);
296            }
297            _ => panic!("Expected ScVal::Bool, got {:?}", scval),
298        }
299    }
300
301    #[test]
302    fn test_string_into_scval() {
303        let value = "test string".to_string();
304
305        // Test try_into_val
306        let scval = value.try_into_val().unwrap();
307        match scval {
308            ScVal::String(sc_string) => {
309                let string_value: String = sc_string.to_utf8_string_lossy();
310                assert_eq!(string_value, "test string");
311            }
312            _ => panic!("Expected ScVal::String, got {:?}", scval),
313        }
314
315        // Test into_val
316        let value = "test string".to_string();
317        let scval = value.into_val();
318        match scval {
319            ScVal::String(sc_string) => {
320                let string_value: String = sc_string.to_utf8_string_lossy();
321                assert_eq!(string_value, "test string");
322            }
323            _ => panic!("Expected ScVal::String, got {:?}", scval),
324        }
325    }
326
327    #[test]
328    fn test_bytes_into_scval() {
329        let value = [42u8; 32];
330
331        // Test try_into_val
332        let scval = value.try_into_val().unwrap();
333        match scval {
334            ScVal::Bytes(sc_bytes) => {
335                assert_eq!(sc_bytes.as_slice(), &value);
336            }
337            _ => panic!("Expected ScVal::Bytes, got {:?}", scval),
338        }
339
340        // Test into_val
341        let scval = value.into_val();
342        match scval {
343            ScVal::Bytes(sc_bytes) => {
344                assert_eq!(sc_bytes.as_slice(), &value);
345            }
346            _ => panic!("Expected ScVal::Bytes, got {:?}", scval),
347        }
348    }
349
350    #[test]
351    fn test_duration_into_scval() {
352        let value = Duration::from_secs(42);
353
354        // Test try_into_val
355        let scval = value.try_into_val().unwrap();
356        match scval {
357            ScVal::Duration(xdr_duration) => {
358                assert_eq!(xdr_duration.0, 42);
359            }
360            _ => panic!("Expected ScVal::Duration, got {:?}", scval),
361        }
362
363        // Test into_val
364        let scval = value.into_val();
365        match scval {
366            ScVal::Duration(xdr_duration) => {
367                assert_eq!(xdr_duration.0, 42);
368            }
369            _ => panic!("Expected ScVal::Duration, got {:?}", scval),
370        }
371    }
372
373    #[test]
374    fn test_vec_scval_into_scval() {
375        let values = vec![ScVal::U32(1), ScVal::I32(-1), ScVal::Bool(true)];
376
377        // Test try_into_val
378        let scval = values.try_into_val().unwrap();
379        match scval {
380            ScVal::Vec(Some(sc_vec)) => {
381                let vec_values: Vec<ScVal> = sc_vec.0.to_vec();
382                assert_eq!(vec_values.len(), 3);
383                assert_eq!(vec_values[0], ScVal::U32(1));
384                assert_eq!(vec_values[1], ScVal::I32(-1));
385                assert_eq!(vec_values[2], ScVal::Bool(true));
386            }
387            _ => panic!("Expected ScVal::Vec, got {:?}", scval),
388        }
389
390        // Test into_val
391        let values = vec![ScVal::U32(1), ScVal::I32(-1), ScVal::Bool(true)];
392        let scval = values.into_val();
393        match scval {
394            ScVal::Vec(Some(sc_vec)) => {
395                let vec_values: Vec<ScVal> = sc_vec.0.to_vec();
396                assert_eq!(vec_values.len(), 3);
397                assert_eq!(vec_values[0], ScVal::U32(1));
398                assert_eq!(vec_values[1], ScVal::I32(-1));
399                assert_eq!(vec_values[2], ScVal::Bool(true));
400            }
401            _ => panic!("Expected ScVal::Vec, got {:?}", scval),
402        }
403    }
404
405    #[test]
406    fn test_string_conversion_error() {
407        let result = StringM::<{ u32::MAX }>::try_from("test".to_string());
408        assert!(result.is_ok(), "Small string should convert successfully");
409    }
410
411    #[test]
412    fn test_vec_conversion_error() {
413        let small_vec = vec![ScVal::U32(1), ScVal::U32(2)];
414        let result: Result<VecM<ScVal, { u32::MAX }>, _> = VecM::try_from(&small_vec);
415        assert!(result.is_ok(), "Small vector should convert successfully");
416    }
417}