templar_common/oracle/redstone/
feed_id.rs

1use std::{fmt::Display, ops::Deref, sync::Arc};
2
3use near_sdk::near;
4
5#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6#[near(serializers = [json, borsh])]
7pub struct FeedId(Arc<str>);
8
9impl Deref for FeedId {
10    type Target = str;
11
12    fn deref(&self) -> &Self::Target {
13        &self.0
14    }
15}
16
17impl Clone for FeedId {
18    fn clone(&self) -> Self {
19        Self(Arc::clone(&self.0))
20    }
21}
22
23impl AsRef<str> for FeedId {
24    fn as_ref(&self) -> &str {
25        &self.0
26    }
27}
28
29impl From<&str> for FeedId {
30    fn from(value: &str) -> Self {
31        Self(Arc::from(value))
32    }
33}
34
35impl From<String> for FeedId {
36    fn from(value: String) -> Self {
37        Self(Arc::from(value))
38    }
39}
40
41impl Display for FeedId {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        self.0.fmt(f)
44    }
45}
46
47impl From<redstone::FeedId> for FeedId {
48    fn from(id: redstone::FeedId) -> Self {
49        let bytes = id.to_array();
50
51        let mut end = bytes.len();
52        while end > 0 && bytes[end - 1] == 0 {
53            end -= 1;
54        }
55
56        // Feed IDs from RedStone are expected to be valid UTF-8 (ASCII strings).
57        // Using lossy conversion here is intentional - invalid bytes would indicate
58        // a misconfigured or corrupted feed ID from the oracle.
59        Self(Arc::from(String::from_utf8_lossy(&bytes[..end])))
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    #[test]
66    fn test_feed_to_string_simple() {
67        let btc_feed_id_array: [u8; 32] = [
68            66, 84, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69            0, 0, 0, 0,
70        ];
71
72        let btc_feed_id = redstone::FeedId::from(btc_feed_id_array);
73
74        let convert_btc_feed_id_to_string = super::FeedId::from(btc_feed_id);
75
76        assert_eq!(convert_btc_feed_id_to_string, "BTC".into());
77
78        let non_btc_feed_id_array: [u8; 32] = [
79            66, 84, 67, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80            0, 0, 0, 0,
81        ];
82
83        let non_btc_feed_id = redstone::FeedId::from(non_btc_feed_id_array);
84
85        let convert_non_btc_feed_id_to_string = super::FeedId::from(non_btc_feed_id);
86
87        assert_eq!(convert_non_btc_feed_id_to_string, "BTC\0C".into());
88    }
89
90    #[test]
91    fn test_feed_to_string_all_zero_bytes() {
92        let feed_id = redstone::FeedId::from([0u8; 32]);
93        let converted = super::FeedId::from(feed_id);
94        assert_eq!(converted, "".into());
95    }
96
97    #[test]
98    fn test_feed_to_string_invalid_utf8_is_lossy() {
99        let mut bytes = [0u8; 32];
100        bytes[0] = 0xFF;
101        bytes[1] = b'B';
102        let feed_id = redstone::FeedId::from(bytes);
103        let converted = super::FeedId::from(feed_id);
104        assert_eq!(converted.as_ref(), "�B");
105    }
106}