tempo_primitives/
address.rs1use alloy_primitives::{Address, FixedBytes, hex};
2
3pub const TIP20_TOKEN_PREFIX: [u8; 12] = hex!("20C000000000000000000000");
6
7pub fn is_tip20_prefix(addr: Address) -> bool {
12 addr.as_slice().starts_with(&TIP20_TOKEN_PREFIX)
13}
14
15pub type MasterId = FixedBytes<4>;
17
18pub type UserTag = FixedBytes<6>;
20
21pub trait TempoAddressExt {
23 const TIP20_PREFIX: [u8; 12];
27
28 const VIRTUAL_MAGIC: [u8; 10];
32
33 fn is_tip20(&self) -> bool;
40
41 fn is_virtual(&self) -> bool;
46
47 fn is_valid_master(&self) -> bool;
49
50 fn decode_virtual(&self) -> Option<(MasterId, UserTag)>;
54
55 fn new_virtual(master_id: MasterId, user_tag: UserTag) -> Self;
59}
60
61impl TempoAddressExt for Address {
62 const TIP20_PREFIX: [u8; 12] = TIP20_TOKEN_PREFIX;
63 const VIRTUAL_MAGIC: [u8; 10] = [0xFD; 10];
64
65 fn is_tip20(&self) -> bool {
66 is_tip20_prefix(*self)
67 }
68
69 fn is_virtual(&self) -> bool {
70 self.as_slice()[4..14] == Self::VIRTUAL_MAGIC
71 }
72
73 fn is_valid_master(&self) -> bool {
74 !self.is_zero() && !self.is_virtual() && !self.is_tip20()
75 }
76
77 fn decode_virtual(&self) -> Option<(MasterId, UserTag)> {
78 if !self.is_virtual() {
79 return None;
80 }
81 let bytes = self.as_slice();
82 Some((
83 MasterId::from_slice(&bytes[0..4]),
84 UserTag::from_slice(&bytes[14..20]),
85 ))
86 }
87
88 fn new_virtual(master_id: MasterId, user_tag: UserTag) -> Self {
89 let mut bytes = [0u8; 20];
90 bytes[0..4].copy_from_slice(master_id.as_slice());
91 bytes[4..14].copy_from_slice(&Self::VIRTUAL_MAGIC);
92 bytes[14..20].copy_from_slice(user_tag.as_slice());
93 Self::from(bytes)
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use alloy_primitives::address;
101
102 #[test]
103 fn is_tip20_prefix_variations() {
104 let mut bytes = [0u8; 20];
106 bytes[..12].copy_from_slice(&TIP20_TOKEN_PREFIX);
107 let tip20_addr = Address::from(bytes);
108 assert!(is_tip20_prefix(tip20_addr));
109 assert!(tip20_addr.is_tip20());
110
111 assert!(!Address::ZERO.is_tip20());
113
114 assert!(!address!("0x1111111111111111111111111111111111111111").is_tip20());
116
117 let mut wrong = [0u8; 20];
119 wrong[0] = TIP20_TOKEN_PREFIX[0];
120 assert!(!is_tip20_prefix(Address::from(wrong)));
122 }
123
124 #[test]
125 fn virtual_address_variations() {
126 let master_id = MasterId::from([0xAA, 0xBB, 0xCC, 0xDD]);
127 let user_tag = UserTag::from([1, 2, 3, 4, 5, 6]);
128
129 let vaddr = Address::new_virtual(master_id, user_tag);
131 assert!(vaddr.is_virtual());
132 let (decoded_master, decoded_tag) = vaddr.decode_virtual().unwrap();
133 assert_eq!(decoded_master, master_id);
134 assert_eq!(decoded_tag, user_tag);
135
136 assert!(Address::ZERO.decode_virtual().is_none());
138 assert!(!Address::ZERO.is_virtual());
139
140 let zero_vaddr = Address::new_virtual(MasterId::ZERO, UserTag::ZERO);
142 assert!(zero_vaddr.is_virtual());
143 let (m, t) = zero_vaddr.decode_virtual().unwrap();
144 assert_eq!(m, MasterId::ZERO);
145 assert_eq!(t, UserTag::ZERO);
146 }
147
148 #[test]
149 fn is_valid_master_variations() {
150 let regular = address!("0x1111111111111111111111111111111111111111");
152 assert!(regular.is_valid_master());
153
154 assert!(!Address::ZERO.is_valid_master());
156
157 let vaddr = Address::new_virtual(
159 MasterId::from([1, 2, 3, 4]),
160 UserTag::from([5, 6, 7, 8, 9, 10]),
161 );
162 assert!(!vaddr.is_valid_master());
163
164 let mut tip20_bytes = [0u8; 20];
166 tip20_bytes[..12].copy_from_slice(&TIP20_TOKEN_PREFIX);
167 assert!(!Address::from(tip20_bytes).is_valid_master());
168 }
169}