1use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
2use scale_info::TypeInfo;
3
4pub type AppAgentId = u32;
6pub type TransactionalId = u32;
7
8pub type AddressIdentifierType = u8;
10pub const APP_AGENT_ADDRESS_IDENTIFIER: AddressIdentifierType = 1;
11pub const TRANSACTIONAL_ADDRESS_IDENTIFIER: AddressIdentifierType = 2;
12pub const NAMED_ADDRESS_IDENTIFIER: AddressIdentifierType = 3;
13
14#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
16#[cfg_attr(feature = "std", derive(Hash))]
17pub struct AccountId32([u8; 32]);
18
19impl AccountId32 {
20 pub const fn new(inner: [u8; 32]) -> Self {
25 Self(inner)
26 }
27
28 pub fn inner(&self) -> &[u8; 32] {
29 &self.0
30 }
31}
32
33impl AsRef<[u8; 32]> for AccountId32 {
34 fn as_ref(&self) -> &[u8; 32] {
35 &self.0
36 }
37}
38
39impl From<[u8; 32]> for AccountId32 {
40 fn from(x: [u8; 32]) -> Self {
41 Self::new(x)
42 }
43}
44
45impl From<AccountId32> for [u8; 32] {
46 fn from(x: AccountId32) -> [u8; 32] {
47 x.0
48 }
49}
50
51#[derive(
52 Clone,
53 Encode,
54 Decode,
55 Debug,
56 Eq,
57 PartialEq,
58 Ord,
59 PartialOrd,
60 Default,
61 Copy,
62 TypeInfo,
63 MaxEncodedLen,
64)]
65pub struct AddressName([u8; 10]);
66
67impl AddressName {
68 const ALLOWED_CHARS: &'static [u8] =
69 b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-#";
70
71 pub fn new(input: &[u8]) -> Result<Self, &'static str> {
72 if input.len() != 10 {
73 return Err("Input must be exactly 10 bytes long");
74 }
75 if input
76 .iter()
77 .all(|&b| AddressName::ALLOWED_CHARS.contains(&b))
78 {
79 let mut array = [0u8; 10];
80 array.copy_from_slice(input);
81 Ok(AddressName(array))
82 } else {
83 Err("Input contains invalid characters")
84 }
85 }
86
87 pub fn as_bytes(&self) -> &[u8; 10] {
88 &self.0
89 }
90
91 pub fn starts_with(&self, prefix: &[u8]) -> bool {
92 self.0.starts_with(prefix)
93 }
94}
95
96impl TryFrom<&[u8]> for AddressName {
97 type Error = &'static str;
98
99 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
100 AddressName::new(value)
101 }
102}
103
104#[derive(Eq, PartialEq, Debug, Clone, Copy)]
112pub enum AddressType {
113 Regular,
114 AppAgent,
115 Transactional,
116 Named,
117}
118
119#[derive(Eq, PartialEq, Debug)]
128pub struct BlockchainAddressInfo {
129 pub account_id: AccountId32,
130 pub address_type: AddressType,
131 pub app_agent_id: Option<AppAgentId>,
132 pub ta_id: Option<TransactionalId>,
133 pub address_name: Option<AddressName>,
134}
135
136impl BlockchainAddressInfo {
137 pub fn from_regular_account(account_id: AccountId32) -> Self {
140 Self {
141 account_id,
142 address_type: AddressType::Regular,
143 app_agent_id: None,
144 ta_id: None,
145 address_name: None,
146 }
147 }
148
149 pub fn from_app_agent_account(account_id: AccountId32, app_agent_id: AppAgentId) -> Self {
152 Self {
153 account_id,
154 address_type: AddressType::AppAgent,
155 app_agent_id: Some(app_agent_id),
156 ta_id: None,
157 address_name: None,
158 }
159 }
160
161 pub fn from_ta_account(
164 account_id: AccountId32,
165 app_agent_id: AppAgentId,
166 ta_id: TransactionalId,
167 ) -> Self {
168 Self {
169 account_id,
170 address_type: AddressType::Transactional,
171 app_agent_id: Some(app_agent_id),
172 ta_id: Some(ta_id),
173 address_name: None,
174 }
175 }
176
177 pub fn from_named_account(
180 account_id: AccountId32,
181 app_agent_id: AppAgentId,
182 name: AddressName,
183 ) -> Self {
184 Self {
185 account_id,
186 address_type: AddressType::Named,
187 app_agent_id: Some(app_agent_id),
188 ta_id: None,
189 address_name: Some(name),
190 }
191 }
192
193 pub fn is_keyless(&self) -> bool {
195 self.address_type != AddressType::Regular
196 }
197}
198
199pub(super) enum BlockchainAccountIds {
201 Regular,
202 AppAgent(AppAgentId),
203 Transactional((AppAgentId, TransactionalId)),
204 Named((AppAgentId, AddressName)),
205}
206
207#[cfg(test)]
208mod tests_address_name {
209 use super::*;
210
211 #[test]
212 fn test_valid_address_name() {
213 let valid_address = b"abcdEF1234";
214 assert!(AddressName::new(valid_address).is_ok());
215 }
216
217 #[test]
218 fn test_invalid_length() {
219 let short_address = b"abcdEF123";
220 let long_address = b"abcdEF12345";
221 assert_eq!(
222 AddressName::new(short_address).unwrap_err(),
223 "Input must be exactly 10 bytes long"
224 );
225 assert_eq!(
226 AddressName::new(long_address).unwrap_err(),
227 "Input must be exactly 10 bytes long"
228 );
229 }
230
231 #[test]
232 fn test_invalid_characters() {
233 let invalid_address1 = b"abcdEF@123";
234 let invalid_address2 = b"abcdEF*123";
235 let invalid_address3 = b"abcdEF/123";
236 assert_eq!(
237 AddressName::new(invalid_address1).unwrap_err(),
238 "Input contains invalid characters"
239 );
240 assert_eq!(
241 AddressName::new(invalid_address2).unwrap_err(),
242 "Input contains invalid characters"
243 );
244 assert_eq!(
245 AddressName::new(invalid_address3).unwrap_err(),
246 "Input contains invalid characters"
247 );
248 }
249
250 #[test]
251 fn test_edge_cases() {
252 let valid_address1 = b"0000000000";
253 let valid_address2 = b"##########";
254 let valid_address3 = b"abcdEFghij";
255 let valid_address4 = b"1234567890";
256
257 assert!(AddressName::new(valid_address1).is_ok());
258 assert!(AddressName::new(valid_address2).is_ok());
259 assert!(AddressName::new(valid_address3).is_ok());
260 assert!(AddressName::new(valid_address4).is_ok());
261 }
262}