1use blake2::Blake2b512;
32use serde::{Deserialize, Serialize};
33use sha3::{Digest, Keccak256};
34use thiserror::Error;
35
36#[derive(Error, Debug, Clone, PartialEq, Eq)]
38pub enum ValidationError {
39 #[error("Invalid EVM address format: {0}")]
41 InvalidEvmAddress(String),
42
43 #[error("EIP-55 checksum validation failed for address: {0}")]
45 InvalidChecksum(String),
46
47 #[error("Invalid chain ID: expected {expected} for {chain}, got {actual}")]
49 InvalidChainId {
50 chain: String,
51 expected: u64,
52 actual: u64,
53 },
54
55 #[error("Chain ID not available for chain: {0}")]
57 ChainIdNotFound(String),
58
59 #[error("Invalid Substrate SS58 address format: {0}")]
61 InvalidSubstrateAddress(String),
62
63 #[error("SS58 checksum validation failed for address: {0}")]
65 InvalidSs58Checksum(String),
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
70pub enum ChainType {
71 Substrate,
73 Evm,
75 Hybrid,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
81pub enum Chain {
82 Polkadot,
85 Kusama,
87
88 Moonbeam,
91 Astar,
93 Acala,
95 Phala,
97 Bifrost,
99 Westend,
101 Paseo,
103
104 Ethereum,
107 BinanceSmartChain,
109 Polygon,
111 Avalanche,
113
114 Arbitrum,
117 Optimism,
119 ZkSync,
121 Base,
123}
124
125impl Chain {
126 pub fn chain_type(&self) -> ChainType {
128 match self {
129 Chain::Polkadot
131 | Chain::Kusama
132 | Chain::Acala
133 | Chain::Phala
134 | Chain::Bifrost
135 | Chain::Westend
136 | Chain::Paseo => ChainType::Substrate,
137
138 Chain::Ethereum
140 | Chain::BinanceSmartChain
141 | Chain::Polygon
142 | Chain::Avalanche
143 | Chain::Arbitrum
144 | Chain::Optimism
145 | Chain::ZkSync
146 | Chain::Base => ChainType::Evm,
147
148 Chain::Moonbeam | Chain::Astar => ChainType::Hybrid,
150 }
151 }
152
153 pub fn name(&self) -> &str {
155 match self {
156 Chain::Polkadot => "Polkadot",
158 Chain::Kusama => "Kusama",
159 Chain::Acala => "Acala",
160 Chain::Phala => "Phala",
161 Chain::Bifrost => "Bifrost",
162 Chain::Westend => "Westend",
163 Chain::Paseo => "Paseo",
164
165 Chain::Ethereum => "Ethereum",
167 Chain::BinanceSmartChain => "Binance Smart Chain",
168 Chain::Polygon => "Polygon",
169 Chain::Avalanche => "Avalanche",
170
171 Chain::Arbitrum => "Arbitrum",
173 Chain::Optimism => "Optimism",
174 Chain::ZkSync => "zkSync",
175 Chain::Base => "Base",
176
177 Chain::Moonbeam => "Moonbeam",
179 Chain::Astar => "Astar",
180 }
181 }
182
183 pub fn default_endpoint(&self) -> &str {
185 match self {
186 Chain::Polkadot => "wss://polkadot.api.onfinality.io/public-ws",
188 Chain::Kusama => "wss://kusama.api.onfinality.io/public-ws",
189 Chain::Acala => "wss://acala.api.onfinality.io/public-ws",
190 Chain::Phala => "wss://phala.api.onfinality.io/public-ws",
191 Chain::Bifrost => "wss://bifrost-polkadot.api.onfinality.io/public-ws",
192 Chain::Westend => "wss://westend-rpc.polkadot.io",
193 Chain::Paseo => "wss://paseo.rpc.amforc.com",
194
195 Chain::Ethereum => "https://eth.llamarpc.com",
197 Chain::BinanceSmartChain => "https://bsc.publicnode.com",
198 Chain::Polygon => "https://polygon-rpc.com",
199 Chain::Avalanche => "https://api.avax.network/ext/bc/C/rpc",
200
201 Chain::Arbitrum => "https://arb1.arbitrum.io/rpc",
203 Chain::Optimism => "https://mainnet.optimism.io",
204 Chain::ZkSync => "https://mainnet.era.zksync.io",
205 Chain::Base => "https://mainnet.base.org",
206
207 Chain::Moonbeam => "wss://moonbeam.api.onfinality.io/public-ws",
209 Chain::Astar => "wss://astar.api.onfinality.io/public-ws",
210 }
211 }
212
213 pub fn rpc_endpoints(&self) -> Vec<&str> {
215 match self {
216 Chain::Polkadot => vec![
218 "wss://polkadot.api.onfinality.io/public-ws",
219 "wss://rpc.ibp.network/polkadot",
220 "wss://polkadot.dotters.network",
221 ],
222 Chain::Kusama => vec![
223 "wss://kusama.api.onfinality.io/public-ws",
224 "wss://rpc.ibp.network/kusama",
225 "wss://kusama.dotters.network",
226 ],
227 Chain::Westend => vec![
228 "wss://westend-rpc.polkadot.io",
229 "wss://rpc.ibp.network/westend",
230 "wss://westend.dotters.network",
231 ],
232 _ => vec![self.default_endpoint()],
234 }
235 }
236
237 pub fn is_layer2(&self) -> bool {
239 matches!(
240 self,
241 Chain::Arbitrum | Chain::Optimism | Chain::ZkSync | Chain::Base
242 )
243 }
244
245 pub fn supports_smart_contracts(&self) -> bool {
247 match self.chain_type() {
248 ChainType::Evm => true,
249 ChainType::Hybrid => true,
250 ChainType::Substrate => matches!(
251 self,
252 Chain::Acala | Chain::Phala | Chain::Moonbeam | Chain::Astar
253 ),
254 }
255 }
256
257 pub fn is_testnet(&self) -> bool {
259 matches!(self, Chain::Westend | Chain::Paseo)
260 }
261
262 pub fn from_str_case_insensitive(s: &str) -> Option<Self> {
264 match s.to_lowercase().as_str() {
265 "polkadot" => Some(Chain::Polkadot),
267 "kusama" => Some(Chain::Kusama),
268 "acala" => Some(Chain::Acala),
269 "phala" => Some(Chain::Phala),
270 "bifrost" => Some(Chain::Bifrost),
271 "westend" => Some(Chain::Westend),
272 "paseo" => Some(Chain::Paseo),
273
274 "ethereum" | "eth" => Some(Chain::Ethereum),
276 "binance" | "bsc" | "binancesmartchain" => Some(Chain::BinanceSmartChain),
277 "polygon" | "matic" => Some(Chain::Polygon),
278 "avalanche" | "avax" => Some(Chain::Avalanche),
279
280 "arbitrum" | "arb" => Some(Chain::Arbitrum),
282 "optimism" | "op" => Some(Chain::Optimism),
283 "zksync" => Some(Chain::ZkSync),
284 "base" => Some(Chain::Base),
285
286 "moonbeam" => Some(Chain::Moonbeam),
288 "astar" => Some(Chain::Astar),
289
290 _ => None,
291 }
292 }
293
294 pub fn is_substrate_endpoint(endpoint: &str) -> bool {
296 endpoint.starts_with("ws://") || endpoint.starts_with("wss://")
297 }
298
299 pub fn is_evm_endpoint(endpoint: &str) -> bool {
301 endpoint.starts_with("http://") || endpoint.starts_with("https://")
302 }
303
304 pub fn chain_id(&self) -> Option<u64> {
309 match self {
310 Chain::Polkadot
312 | Chain::Kusama
313 | Chain::Acala
314 | Chain::Phala
315 | Chain::Bifrost
316 | Chain::Westend
317 | Chain::Paseo => None,
318
319 Chain::Ethereum => Some(1),
321 Chain::BinanceSmartChain => Some(56),
322 Chain::Polygon => Some(137),
323 Chain::Avalanche => Some(43114),
324
325 Chain::Arbitrum => Some(42161),
327 Chain::Optimism => Some(10),
328 Chain::ZkSync => Some(324),
329 Chain::Base => Some(8453),
330
331 Chain::Moonbeam => Some(1284),
333 Chain::Astar => Some(592),
334 }
335 }
336
337 pub fn validate_chain_id(&self, chain_id: u64) -> Result<(), ValidationError> {
342 match self.chain_id() {
343 None => Err(ValidationError::ChainIdNotFound(self.name().to_string())),
344 Some(expected) => {
345 if expected == chain_id {
346 Ok(())
347 } else {
348 Err(ValidationError::InvalidChainId {
349 chain: self.name().to_string(),
350 expected,
351 actual: chain_id,
352 })
353 }
354 }
355 }
356 }
357}
358
359fn is_valid_evm_format(addr: &str) -> bool {
361 if !addr.starts_with("0x") {
362 return false;
363 }
364
365 let hex_part = &addr[2..];
366 hex_part.len() == 40 && hex_part.chars().all(|c| c.is_ascii_hexdigit())
367}
368
369fn to_checksum_address(addr: &str) -> String {
374 let addr_lower = addr.trim_start_matches("0x").to_lowercase();
376
377 let mut hasher = Keccak256::new();
379 hasher.update(addr_lower.as_bytes());
380 let hash = hasher.finalize();
381
382 let mut result = String::from("0x");
384 for (i, ch) in addr_lower.chars().enumerate() {
385 if ch.is_ascii_digit() {
386 result.push(ch);
387 } else {
388 let hash_byte = hash[i / 2];
390 let nibble = if i % 2 == 0 {
391 hash_byte >> 4
392 } else {
393 hash_byte & 0x0f
394 };
395
396 if nibble >= 8 {
397 result.push(ch.to_ascii_uppercase());
398 } else {
399 result.push(ch);
400 }
401 }
402 }
403
404 result
405}
406
407fn validate_eip55_checksum(addr: &str) -> bool {
412 let hex_part = &addr[2..];
413
414 let all_lower = hex_part.chars().all(|c| !c.is_ascii_uppercase());
417 let all_upper = hex_part.chars().all(|c| !c.is_ascii_lowercase());
418
419 if all_lower || all_upper {
420 return true;
421 }
422
423 let checksummed = to_checksum_address(addr);
425 addr == checksummed
426}
427
428fn validate_ss58_address(addr: &str) -> bool {
435 let decoded = match bs58::decode(addr).into_vec() {
437 Ok(d) => d,
438 Err(_) => return false,
439 };
440
441 if decoded.len() < 3 {
443 return false;
444 }
445
446 let checksum_len =
448 if decoded.len() == 3 || decoded.len() == 4 || decoded.len() == 6 || decoded.len() == 10 {
449 1
450 } else {
451 2
452 };
453
454 let body_len = decoded.len() - checksum_len;
455 let body = &decoded[..body_len];
456 let checksum = &decoded[body_len..];
457
458 let mut hasher = Blake2b512::new();
460 hasher.update(b"SS58PRE");
461 hasher.update(body);
462 let hash = hasher.finalize();
463
464 &hash[..checksum_len] == checksum
466}
467
468#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
470pub enum Address {
471 Substrate(String),
473 Evm(String),
475}
476
477impl Address {
478 pub fn substrate_checked(addr: impl Into<String>) -> Result<Self, ValidationError> {
501 let addr_str = addr.into();
502
503 if !validate_ss58_address(&addr_str) {
505 return Err(ValidationError::InvalidSubstrateAddress(addr_str));
506 }
507
508 Ok(Address::Substrate(addr_str))
509 }
510
511 pub fn substrate(addr: impl Into<String>) -> Self {
519 Address::Substrate(addr.into())
520 }
521
522 pub fn evm_checked(addr: impl Into<String>) -> Result<Self, ValidationError> {
548 let addr_str = addr.into();
549
550 if !is_valid_evm_format(&addr_str) {
552 return Err(ValidationError::InvalidEvmAddress(addr_str));
553 }
554
555 if !validate_eip55_checksum(&addr_str) {
557 return Err(ValidationError::InvalidChecksum(addr_str));
558 }
559
560 Ok(Address::Evm(addr_str))
561 }
562
563 pub fn evm(addr: impl Into<String>) -> Self {
571 Address::Evm(addr.into())
572 }
573
574 pub fn to_checksum(&self) -> String {
579 match self {
580 Address::Evm(addr) => to_checksum_address(addr),
581 Address::Substrate(addr) => addr.clone(),
582 }
583 }
584
585 pub fn as_str(&self) -> &str {
587 match self {
588 Address::Substrate(s) | Address::Evm(s) => s,
589 }
590 }
591
592 pub fn validate(&self) -> Result<(), ValidationError> {
597 match self {
598 Address::Evm(addr) => {
599 if !is_valid_evm_format(addr) {
600 return Err(ValidationError::InvalidEvmAddress(addr.clone()));
601 }
602 if !validate_eip55_checksum(addr) {
603 return Err(ValidationError::InvalidChecksum(addr.clone()));
604 }
605 Ok(())
606 }
607 Address::Substrate(_) => Ok(()), }
609 }
610}
611
612#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
614pub enum TransactionStatus {
615 Pending,
620 InMempool,
625 Confirmed {
627 block_hash: String,
629 block_number: Option<u64>,
631 },
632 Finalized {
634 block_hash: String,
636 block_number: u64,
638 },
639 Failed {
641 error: String,
643 },
644 Unknown,
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
679pub struct Event {
680 pub name: String,
682 pub data: serde_json::Value,
684 pub block_number: Option<u64>,
686 pub tx_hash: Option<String>,
688 pub index: Option<u32>,
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize)]
698pub struct EventFilter {
699 pub event_names: Option<Vec<String>>,
704 pub addresses: Option<Vec<Address>>,
709 pub from_block: Option<u64>,
714 pub to_block: Option<u64>,
719}
720
721#[derive(Debug, Clone, Serialize, Deserialize)]
723pub struct CrossChainTransaction {
724 pub id: String,
726 pub source_chain: Chain,
728 pub destination_chain: Chain,
730 pub source_tx_hash: Option<String>,
732 pub destination_tx_hash: Option<String>,
734 pub status: TransactionStatus,
736 pub timestamp: u64,
738}
739
740#[cfg(test)]
741mod tests {
742 use super::*;
743
744 #[test]
745 fn test_chain_type() {
746 assert_eq!(Chain::Polkadot.chain_type(), ChainType::Substrate);
747 assert_eq!(Chain::Ethereum.chain_type(), ChainType::Evm);
748 assert_eq!(Chain::Moonbeam.chain_type(), ChainType::Hybrid);
749 }
750
751 #[test]
752 fn test_address_creation() {
753 let sub_addr = Address::substrate("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
754 assert!(matches!(sub_addr, Address::Substrate(_)));
755
756 let evm_addr = Address::evm("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7");
757 assert!(matches!(evm_addr, Address::Evm(_)));
758 }
759
760 #[test]
761 fn test_chain_id() {
762 assert_eq!(Chain::Ethereum.chain_id(), Some(1));
764 assert_eq!(Chain::BinanceSmartChain.chain_id(), Some(56));
765 assert_eq!(Chain::Polygon.chain_id(), Some(137));
766 assert_eq!(Chain::Avalanche.chain_id(), Some(43114));
767 assert_eq!(Chain::Arbitrum.chain_id(), Some(42161));
768 assert_eq!(Chain::Optimism.chain_id(), Some(10));
769 assert_eq!(Chain::ZkSync.chain_id(), Some(324));
770 assert_eq!(Chain::Base.chain_id(), Some(8453));
771
772 assert_eq!(Chain::Moonbeam.chain_id(), Some(1284));
774 assert_eq!(Chain::Astar.chain_id(), Some(592));
775
776 assert_eq!(Chain::Polkadot.chain_id(), None);
778 assert_eq!(Chain::Kusama.chain_id(), None);
779 assert_eq!(Chain::Westend.chain_id(), None);
780 assert_eq!(Chain::Paseo.chain_id(), None);
781 }
782
783 #[test]
784 fn test_chain_id_validation() {
785 assert!(Chain::Ethereum.validate_chain_id(1).is_ok());
787 assert!(Chain::BinanceSmartChain.validate_chain_id(56).is_ok());
788 assert!(Chain::Polygon.validate_chain_id(137).is_ok());
789
790 assert!(Chain::Ethereum.validate_chain_id(56).is_err());
792 assert!(Chain::Polygon.validate_chain_id(1).is_err());
793
794 assert!(Chain::Polkadot.validate_chain_id(1).is_err());
796 }
797
798 #[test]
799 fn test_eip55_valid_checksummed_addresses() {
800 let valid_addresses = vec![
802 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
803 "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
804 "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
805 "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
806 ];
807
808 for addr in valid_addresses {
809 let result = Address::evm_checked(addr);
810 assert!(
811 result.is_ok(),
812 "Address {} should be valid, got error: {:?}",
813 addr,
814 result.err()
815 );
816 }
817 }
818
819 #[test]
820 fn test_eip55_lowercase_addresses() {
821 let lowercase_addr = "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed";
823 assert!(Address::evm_checked(lowercase_addr).is_ok());
824
825 let lowercase_addr2 = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359";
826 assert!(Address::evm_checked(lowercase_addr2).is_ok());
827 }
828
829 #[test]
830 fn test_eip55_uppercase_addresses() {
831 let uppercase_addr = "0x5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED";
833 assert!(Address::evm_checked(uppercase_addr).is_ok());
834 }
835
836 #[test]
837 fn test_eip55_invalid_checksum() {
838 let invalid_addr = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAeD";
840 let result = Address::evm_checked(invalid_addr);
841 assert!(result.is_err());
842 assert!(matches!(
843 result.unwrap_err(),
844 ValidationError::InvalidChecksum(_)
845 ));
846 }
847
848 #[test]
849 fn test_eip55_invalid_format() {
850 let no_prefix = "5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed";
852 let result = Address::evm_checked(no_prefix);
853 assert!(result.is_err());
854 assert!(matches!(
855 result.unwrap_err(),
856 ValidationError::InvalidEvmAddress(_)
857 ));
858
859 let too_short = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeA";
861 let result = Address::evm_checked(too_short);
862 assert!(result.is_err());
863
864 let too_long = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAedAA";
866 let result = Address::evm_checked(too_long);
867 assert!(result.is_err());
868
869 let invalid_hex = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAeG";
871 let result = Address::evm_checked(invalid_hex);
872 assert!(result.is_err());
873 }
874
875 #[test]
876 fn test_to_checksum_address() {
877 let lowercase = "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed";
878 let checksummed = to_checksum_address(lowercase);
879 assert_eq!(checksummed, "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed");
880
881 let lowercase2 = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359";
882 let checksummed2 = to_checksum_address(lowercase2);
883 assert_eq!(checksummed2, "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359");
884 }
885
886 #[test]
887 fn test_address_to_checksum_method() {
888 let addr = Address::evm("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed");
889 assert_eq!(
890 addr.to_checksum(),
891 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
892 );
893
894 let sub_addr = Address::substrate("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
896 assert_eq!(
897 sub_addr.to_checksum(),
898 "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
899 );
900 }
901
902 #[test]
903 fn test_address_validate() {
904 let addr = Address::evm("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed");
906 assert!(addr.validate().is_ok());
907
908 let addr = Address::evm("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed");
910 assert!(addr.validate().is_ok());
911
912 let addr = Address::evm("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAeD");
914 assert!(addr.validate().is_err());
915
916 let addr = Address::evm("invalid");
918 assert!(addr.validate().is_err());
919
920 let addr = Address::substrate("anything");
922 assert!(addr.validate().is_ok());
923 }
924
925 #[test]
926 fn test_is_testnet() {
927 assert!(Chain::Westend.is_testnet());
928 assert!(Chain::Paseo.is_testnet());
929 assert!(!Chain::Polkadot.is_testnet());
930 assert!(!Chain::Ethereum.is_testnet());
931 }
932
933 #[test]
934 fn test_substrate_ss58_validation_valid_addresses() {
935 let valid_addresses = vec![
937 "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
938 "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
939 "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM",
940 ];
941
942 for addr in valid_addresses {
943 let result = Address::substrate_checked(addr);
944 assert!(
945 result.is_ok(),
946 "Address {} should be valid, got error: {:?}",
947 addr,
948 result.err()
949 );
950 }
951 }
952
953 #[test]
954 fn test_substrate_ss58_validation_invalid_addresses() {
955 let invalid_addresses = vec![
957 "invalid", "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQ", "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY123", "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQX", ];
962
963 for addr in invalid_addresses {
964 let result = Address::substrate_checked(addr);
965 assert!(
966 result.is_err(),
967 "Address {} should be invalid but was accepted",
968 addr
969 );
970 }
971 }
972
973 #[test]
974 fn test_substrate_ss58_validation_error_types() {
975 let invalid_format = "not-base58!@#";
977 let result = Address::substrate_checked(invalid_format);
978 assert!(result.is_err());
979 match result.unwrap_err() {
980 ValidationError::InvalidSubstrateAddress(_) => (),
981 _ => panic!("Expected InvalidSubstrateAddress error"),
982 }
983
984 let invalid_checksum = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQX";
986 let result = Address::substrate_checked(invalid_checksum);
987 assert!(result.is_err());
988 }
990
991 #[test]
992 fn test_validation_error_messages() {
993 let invalid_evm = "0xinvalid";
995 let result = Address::evm_checked(invalid_evm);
996 assert!(result.is_err());
997 let err = result.unwrap_err();
998 assert!(err.to_string().contains("Invalid EVM address"));
999
1000 let invalid_checksum = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAeD";
1002 let result = Address::evm_checked(invalid_checksum);
1003 assert!(result.is_err());
1004 let err = result.unwrap_err();
1005 assert!(err.to_string().contains("checksum"));
1006 }
1007
1008 #[test]
1009 fn test_overflow_protection_in_validation() {
1010 let long_string = "a".repeat(1000);
1012 let result = Address::evm_checked(&long_string);
1013 assert!(result.is_err());
1014
1015 let result = Address::substrate_checked(&long_string);
1016 assert!(result.is_err());
1017 }
1018}