1use alloy_primitives::{Address, U256};
21use serde::{Deserialize, Serialize};
22
23pub type AddressPerChain = Vec<(super::chain::SupportedChainId, Address)>;
25
26pub type ApiBaseUrls = Vec<(super::chain::SupportedChainId, String)>;
28
29use super::chain::SupportedChainId;
30
31pub const RAW_FILES_PATH: &str = "https://files.cow.fi/cow-sdk";
35
36pub const RAW_CHAINS_FILES_PATH: &str = "https://files.cow.fi/cow-sdk/chains";
38
39pub const TOKEN_LIST_IMAGES_PATH: &str = "https://files.cow.fi/token-lists/images";
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
49#[repr(u64)]
50pub enum EvmChains {
51 Mainnet = 1,
53 Optimism = 10,
55 Bnb = 56,
57 GnosisChain = 100,
59 Polygon = 137,
61 Base = 8_453,
63 Plasma = 9_745,
65 ArbitrumOne = 42_161,
67 Avalanche = 43_114,
69 Ink = 57_073,
71 Linea = 59_144,
73 Sepolia = 11_155_111,
75}
76
77impl EvmChains {
78 #[must_use]
84 pub const fn as_u64(self) -> u64 {
85 self as u64
86 }
87
88 #[must_use]
98 pub const fn try_from_u64(chain_id: u64) -> Option<Self> {
99 match chain_id {
100 1 => Some(Self::Mainnet),
101 10 => Some(Self::Optimism),
102 56 => Some(Self::Bnb),
103 100 => Some(Self::GnosisChain),
104 137 => Some(Self::Polygon),
105 8_453 => Some(Self::Base),
106 9_745 => Some(Self::Plasma),
107 42_161 => Some(Self::ArbitrumOne),
108 43_114 => Some(Self::Avalanche),
109 57_073 => Some(Self::Ink),
110 59_144 => Some(Self::Linea),
111 11_155_111 => Some(Self::Sepolia),
112 _ => None,
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
119#[repr(u64)]
120pub enum NonEvmChains {
121 Bitcoin = 1_000_000_000,
123 Solana = 1_000_000_001,
125}
126
127impl NonEvmChains {
128 #[must_use]
134 pub const fn as_u64(self) -> u64 {
135 self as u64
136 }
137
138 #[must_use]
148 pub const fn try_from_u64(chain_id: u64) -> Option<Self> {
149 match chain_id {
150 1_000_000_000 => Some(Self::Bitcoin),
151 1_000_000_001 => Some(Self::Solana),
152 _ => None,
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
160#[repr(u64)]
161pub enum AdditionalTargetChainId {
162 Optimism = 10,
164 Bitcoin = 1_000_000_000,
166 Solana = 1_000_000_001,
168}
169
170impl AdditionalTargetChainId {
171 #[must_use]
177 pub const fn as_u64(self) -> u64 {
178 self as u64
179 }
180
181 #[must_use]
191 pub const fn try_from_u64(chain_id: u64) -> Option<Self> {
192 match chain_id {
193 10 => Some(Self::Optimism),
194 1_000_000_000 => Some(Self::Bitcoin),
195 1_000_000_001 => Some(Self::Solana),
196 _ => None,
197 }
198 }
199
200 #[must_use]
206 pub const fn all() -> &'static [Self] {
207 &[Self::Optimism, Self::Bitcoin, Self::Solana]
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
217pub enum TargetChainId {
218 Supported(SupportedChainId),
220 Additional(AdditionalTargetChainId),
222}
223
224impl TargetChainId {
225 #[must_use]
231 pub const fn as_u64(&self) -> u64 {
232 match self {
233 Self::Supported(c) => c.as_u64(),
234 Self::Additional(c) => c.as_u64(),
235 }
236 }
237}
238
239#[derive(Debug, Clone)]
243pub struct ThemedImage {
244 pub light: &'static str,
246 pub dark: &'static str,
248}
249
250#[derive(Debug, Clone)]
252pub struct WebUrl {
253 pub name: &'static str,
255 pub url: &'static str,
257}
258
259#[derive(Debug, Clone, Copy)]
261pub struct ChainContract {
262 pub address: Address,
264 pub block_created: Option<u64>,
266}
267
268#[derive(Debug, Clone)]
270pub struct ChainContracts {
271 pub multicall3: Option<ChainContract>,
273 pub ens_registry: Option<ChainContract>,
275 pub ens_universal_resolver: Option<ChainContract>,
277}
278
279#[derive(Debug, Clone)]
281pub struct ChainRpcUrls {
282 pub http: &'static [&'static str],
284 pub web_socket: Option<&'static [&'static str]>,
286}
287
288#[derive(Debug, Clone)]
293pub struct ChainTokenInfo {
294 pub chain_id: u64,
296 pub address: &'static str,
298 pub decimals: u8,
300 pub name: &'static str,
302 pub symbol: &'static str,
304 pub logo_url: Option<&'static str>,
306}
307
308#[derive(Debug, Clone)]
310pub struct EvmChainInfo {
311 pub id: u64,
313 pub label: &'static str,
315 pub eip155_label: &'static str,
317 pub address_prefix: &'static str,
319 pub native_currency: ChainTokenInfo,
321 pub is_testnet: bool,
323 pub color: &'static str,
325 pub logo: ThemedImage,
327 pub website: WebUrl,
329 pub docs: WebUrl,
331 pub block_explorer: WebUrl,
333 pub bridges: &'static [WebUrl],
335 pub contracts: ChainContracts,
337 pub rpc_urls: ChainRpcUrls,
339 pub is_zk_sync: bool,
341 pub is_under_development: bool,
343 pub is_deprecated: bool,
345}
346
347#[derive(Debug, Clone)]
349pub struct NonEvmChainInfo {
350 pub id: u64,
352 pub label: &'static str,
354 pub address_prefix: &'static str,
356 pub native_currency: ChainTokenInfo,
358 pub is_testnet: bool,
360 pub color: &'static str,
362 pub logo: ThemedImage,
364 pub website: WebUrl,
366 pub docs: WebUrl,
368 pub block_explorer: WebUrl,
370 pub is_under_development: bool,
372 pub is_deprecated: bool,
374}
375
376#[derive(Debug, Clone)]
378pub enum ChainInfo {
379 Evm(EvmChainInfo),
381 NonEvm(NonEvmChainInfo),
383}
384
385impl ChainInfo {
386 #[must_use]
392 pub const fn id(&self) -> u64 {
393 match self {
394 Self::Evm(info) => info.id,
395 Self::NonEvm(info) => info.id,
396 }
397 }
398
399 #[must_use]
405 pub const fn label(&self) -> &'static str {
406 match self {
407 Self::Evm(info) => info.label,
408 Self::NonEvm(info) => info.label,
409 }
410 }
411
412 #[must_use]
418 pub const fn is_under_development(&self) -> bool {
419 match self {
420 Self::Evm(info) => info.is_under_development,
421 Self::NonEvm(info) => info.is_under_development,
422 }
423 }
424
425 #[must_use]
431 pub const fn is_deprecated(&self) -> bool {
432 match self {
433 Self::Evm(info) => info.is_deprecated,
434 Self::NonEvm(info) => info.is_deprecated,
435 }
436 }
437
438 #[must_use]
444 pub const fn is_evm(&self) -> bool {
445 matches!(self, Self::Evm(_))
446 }
447
448 #[must_use]
454 pub const fn is_non_evm(&self) -> bool {
455 matches!(self, Self::NonEvm(_))
456 }
457
458 #[must_use]
464 pub const fn as_evm(&self) -> Option<&EvmChainInfo> {
465 match self {
466 Self::Evm(info) => Some(info),
467 Self::NonEvm(_) => None,
468 }
469 }
470
471 #[must_use]
477 pub const fn as_non_evm(&self) -> Option<&NonEvmChainInfo> {
478 match self {
479 Self::NonEvm(info) => Some(info),
480 Self::Evm(_) => None,
481 }
482 }
483
484 #[must_use]
490 pub const fn native_currency(&self) -> &ChainTokenInfo {
491 match self {
492 Self::Evm(info) => &info.native_currency,
493 Self::NonEvm(info) => &info.native_currency,
494 }
495 }
496}
497
498#[must_use]
519pub const fn is_evm_chain(chain_id: u64) -> bool {
520 EvmChains::try_from_u64(chain_id).is_some()
521}
522
523#[must_use]
541pub const fn is_non_evm_chain(chain_id: u64) -> bool {
542 NonEvmChains::try_from_u64(chain_id).is_some()
543}
544
545#[must_use]
567pub const fn is_evm_chain_info(chain_info: &ChainInfo) -> bool {
568 chain_info.is_evm()
569}
570
571#[must_use]
593pub const fn is_non_evm_chain_info(chain_info: &ChainInfo) -> bool {
594 chain_info.is_non_evm()
595}
596
597#[must_use]
614pub const fn is_btc_chain(chain_id: u64) -> bool {
615 chain_id == NonEvmChains::Bitcoin as u64
616}
617
618#[must_use]
635pub const fn is_supported_chain(chain_id: u64) -> bool {
636 SupportedChainId::try_from_u64(chain_id).is_some()
637}
638
639#[must_use]
657pub const fn is_additional_target_chain(chain_id: u64) -> bool {
658 AdditionalTargetChainId::try_from_u64(chain_id).is_some()
659}
660
661#[must_use]
680pub const fn is_target_chain_id(chain_id: u64) -> bool {
681 is_supported_chain(chain_id) || is_additional_target_chain(chain_id)
682}
683
684#[must_use]
700pub fn is_zk_sync_chain(chain_id: u64) -> bool {
701 if !is_evm_chain(chain_id) {
702 return false;
703 }
704 get_chain_info(chain_id)
705 .and_then(|info| info.as_evm().cloned())
706 .is_some_and(|evm| evm.is_zk_sync)
707}
708
709#[must_use]
725pub fn is_chain_under_development(chain_id: u64) -> bool {
726 get_chain_info(chain_id).is_some_and(|info| info.is_under_development())
727}
728
729#[must_use]
746pub fn is_chain_deprecated(chain_id: u64) -> bool {
747 get_chain_info(chain_id).is_some_and(|info| info.is_deprecated())
748}
749
750#[must_use]
775pub const fn get_chain_info(chain_id: u64) -> Option<ChainInfo> {
776 if let Some(supported) = SupportedChainId::try_from_u64(chain_id) {
777 return Some(supported_chain_info(supported));
778 }
779
780 if let Some(additional) = AdditionalTargetChainId::try_from_u64(chain_id) {
781 return Some(additional_target_chain_info(additional));
782 }
783
784 None
785}
786
787#[must_use]
807pub fn map_supported_networks<T>(f: impl Fn(SupportedChainId) -> T) -> Vec<(SupportedChainId, T)> {
808 SupportedChainId::all().iter().map(|&chain| (chain, f(chain))).collect()
809}
810
811#[must_use]
822pub fn map_all_networks<T>(f: impl Fn(TargetChainId) -> T) -> Vec<(TargetChainId, T)> {
823 all_chain_ids().into_iter().map(|id| (id, f(id))).collect()
824}
825
826#[must_use]
838pub fn map_address_to_supported_networks(address: Address) -> Vec<(SupportedChainId, Address)> {
839 map_supported_networks(|_| address)
840}
841
842#[must_use]
848pub fn all_supported_chain_ids() -> Vec<SupportedChainId> {
849 SupportedChainId::all().to_vec()
850}
851
852#[must_use]
858pub fn all_supported_chains() -> Vec<ChainInfo> {
859 SupportedChainId::all().iter().map(|&c| supported_chain_info(c)).collect()
860}
861
862#[must_use]
868pub fn tradable_supported_chain_ids() -> Vec<SupportedChainId> {
869 SupportedChainId::all()
870 .iter()
871 .copied()
872 .filter(|&c| !supported_chain_info(c).is_deprecated())
873 .collect()
874}
875
876#[must_use]
882pub fn tradable_supported_chains() -> Vec<ChainInfo> {
883 SupportedChainId::all()
884 .iter()
885 .copied()
886 .filter(|&c| !supported_chain_info(c).is_deprecated())
887 .map(supported_chain_info)
888 .collect()
889}
890
891#[must_use]
897pub fn all_additional_target_chains() -> Vec<ChainInfo> {
898 AdditionalTargetChainId::all().iter().map(|&c| additional_target_chain_info(c)).collect()
899}
900
901#[must_use]
907pub fn all_additional_target_chain_ids() -> Vec<AdditionalTargetChainId> {
908 AdditionalTargetChainId::all().to_vec()
909}
910
911#[must_use]
917pub fn all_chains() -> Vec<ChainInfo> {
918 let mut chains = all_supported_chains();
919 chains.extend(all_additional_target_chains());
920 chains
921}
922
923#[must_use]
929pub fn all_chain_ids() -> Vec<TargetChainId> {
930 let mut ids: Vec<TargetChainId> =
931 SupportedChainId::all().iter().map(|&c| TargetChainId::Supported(c)).collect();
932 ids.extend(AdditionalTargetChainId::all().iter().map(|&c| TargetChainId::Additional(c)));
933 ids
934}
935
936#[must_use]
948pub const fn supported_chain_info(chain: SupportedChainId) -> ChainInfo {
949 ChainInfo::Evm(evm_chain_detail(chain))
950}
951
952#[must_use]
962pub const fn additional_target_chain_info(chain: AdditionalTargetChainId) -> ChainInfo {
963 match chain {
964 AdditionalTargetChainId::Optimism => ChainInfo::Evm(optimism_chain_info()),
965 AdditionalTargetChainId::Bitcoin => ChainInfo::NonEvm(bitcoin_chain_info()),
966 AdditionalTargetChainId::Solana => ChainInfo::NonEvm(solana_chain_info()),
967 }
968}
969
970const fn evm_chain_detail(chain: SupportedChainId) -> EvmChainInfo {
971 match chain {
972 SupportedChainId::Mainnet => mainnet_chain_info(),
973 SupportedChainId::GnosisChain => gnosis_chain_info(),
974 SupportedChainId::ArbitrumOne => arbitrum_chain_info(),
975 SupportedChainId::Base => base_chain_info(),
976 SupportedChainId::Sepolia => sepolia_chain_info(),
977 SupportedChainId::Polygon => polygon_chain_info(),
978 SupportedChainId::Avalanche => avalanche_chain_info(),
979 SupportedChainId::BnbChain => bnb_chain_info(),
980 SupportedChainId::Linea => linea_chain_info(),
981 SupportedChainId::Lens => lens_chain_info(),
982 SupportedChainId::Plasma => plasma_chain_info(),
983 SupportedChainId::Ink => ink_chain_info(),
984 }
985}
986
987const EVM_NATIVE_ADDR: &str = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
989const BTC_ADDR: &str = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa";
991const SOL_ADDR: &str = "11111111111111111111111111111111";
993
994const MULTICALL3: Address = Address::new([
996 0xca, 0x11, 0xbd, 0xe0, 0x59, 0x77, 0xb3, 0x63, 0x11, 0x67, 0x02, 0x88, 0x62, 0xbe, 0x2a, 0x17,
997 0x39, 0x76, 0xca, 0x11,
998]);
999
1000const fn default_native_currency(chain_id: u64) -> ChainTokenInfo {
1001 ChainTokenInfo {
1002 chain_id,
1003 address: EVM_NATIVE_ADDR,
1004 decimals: 18,
1005 name: "Ether",
1006 symbol: "ETH",
1007 logo_url: Some(
1008 "https://files.cow.fi/token-lists/images/1/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/logo.png",
1009 ),
1010 }
1011}
1012
1013const fn no_contracts() -> ChainContracts {
1014 ChainContracts { multicall3: None, ens_registry: None, ens_universal_resolver: None }
1015}
1016
1017const fn multicall3_only(block_created: u64) -> ChainContracts {
1018 ChainContracts {
1019 multicall3: Some(ChainContract { address: MULTICALL3, block_created: Some(block_created) }),
1020 ens_registry: None,
1021 ens_universal_resolver: None,
1022 }
1023}
1024
1025const fn mainnet_chain_info() -> EvmChainInfo {
1026 EvmChainInfo {
1027 id: 1,
1028 label: "Ethereum",
1029 eip155_label: "Ethereum Mainnet",
1030 address_prefix: "eth",
1031 native_currency: default_native_currency(1),
1032 is_testnet: false,
1033 color: "#62688F",
1034 logo: ThemedImage {
1035 light: "https://files.cow.fi/cow-sdk/chains/images/mainnet-logo.svg",
1036 dark: "https://files.cow.fi/cow-sdk/chains/images/mainnet-logo.svg",
1037 },
1038 website: WebUrl { name: "Ethereum", url: "https://ethereum.org" },
1039 docs: WebUrl { name: "Ethereum Docs", url: "https://ethereum.org/en/developers/docs" },
1040 block_explorer: WebUrl { name: "Etherscan", url: "https://etherscan.io" },
1041 bridges: &[],
1042 contracts: ChainContracts {
1043 multicall3: Some(ChainContract {
1044 address: MULTICALL3,
1045 block_created: Some(14_353_601),
1046 }),
1047 ens_registry: Some(ChainContract {
1048 address: Address::new([
1049 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x2e, 0x07, 0x4e, 0xc6, 0x9a, 0x0d, 0xfb,
1050 0x29, 0x97, 0xba, 0x6c, 0x7d, 0x2e, 0x1e,
1051 ]),
1052 block_created: None,
1053 }),
1054 ens_universal_resolver: Some(ChainContract {
1055 address: Address::new([
1056 0xce, 0x01, 0xf8, 0xee, 0xe7, 0xe4, 0x79, 0xc9, 0x28, 0xf8, 0x91, 0x9a, 0xbd,
1057 0x53, 0xe5, 0x53, 0xa3, 0x6c, 0xef, 0x67,
1058 ]),
1059 block_created: Some(19_258_213),
1060 }),
1061 },
1062 rpc_urls: ChainRpcUrls { http: &["https://eth.merkle.io"], web_socket: None },
1063 is_zk_sync: false,
1064 is_under_development: false,
1065 is_deprecated: false,
1066 }
1067}
1068
1069const fn gnosis_chain_info() -> EvmChainInfo {
1070 EvmChainInfo {
1071 id: 100,
1072 label: "Gnosis",
1073 eip155_label: "Gnosis",
1074 address_prefix: "gno",
1075 native_currency: ChainTokenInfo {
1076 chain_id: 100,
1077 address: EVM_NATIVE_ADDR,
1078 decimals: 18,
1079 name: "xDAI",
1080 symbol: "xDAI",
1081 logo_url: Some(
1082 "https://files.cow.fi/token-lists/images/100/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/logo.png",
1083 ),
1084 },
1085 is_testnet: false,
1086 color: "#07795B",
1087 logo: ThemedImage {
1088 light: "https://files.cow.fi/cow-sdk/chains/images/gnosis-logo.svg",
1089 dark: "https://files.cow.fi/cow-sdk/chains/images/gnosis-logo.svg",
1090 },
1091 website: WebUrl { name: "Gnosis Chain", url: "https://www.gnosischain.com" },
1092 docs: WebUrl { name: "Gnosis Chain Docs", url: "https://docs.gnosischain.com" },
1093 block_explorer: WebUrl { name: "Gnosisscan", url: "https://gnosisscan.io" },
1094 bridges: &[WebUrl { name: "Gnosis Chain Bridge", url: "https://bridge.gnosischain.com" }],
1095 contracts: multicall3_only(21_022_491),
1096 rpc_urls: ChainRpcUrls {
1097 http: &["https://rpc.gnosischain.com"],
1098 web_socket: Some(&["wss://rpc.gnosischain.com/wss"]),
1099 },
1100 is_zk_sync: false,
1101 is_under_development: false,
1102 is_deprecated: false,
1103 }
1104}
1105
1106const fn arbitrum_chain_info() -> EvmChainInfo {
1107 EvmChainInfo {
1108 id: 42_161,
1109 label: "Arbitrum",
1110 eip155_label: "Arbitrum One",
1111 address_prefix: "arb1",
1112 native_currency: default_native_currency(42_161),
1113 is_testnet: false,
1114 color: "#1B4ADD",
1115 logo: ThemedImage {
1116 light: "https://files.cow.fi/cow-sdk/chains/images/arbitrum-logo.svg",
1117 dark: "https://files.cow.fi/cow-sdk/chains/images/arbitrum-logo.svg",
1118 },
1119 website: WebUrl { name: "Arbitrum", url: "https://arbitrum.io" },
1120 docs: WebUrl { name: "Arbitrum Docs", url: "https://docs.arbitrum.io" },
1121 block_explorer: WebUrl { name: "Arbiscan", url: "https://arbiscan.io" },
1122 bridges: &[WebUrl { name: "Arbitrum Bridge", url: "https://bridge.arbitrum.io" }],
1123 contracts: multicall3_only(7_654_707),
1124 rpc_urls: ChainRpcUrls { http: &["https://arb1.arbitrum.io/rpc"], web_socket: None },
1125 is_zk_sync: false,
1126 is_under_development: false,
1127 is_deprecated: false,
1128 }
1129}
1130
1131const fn base_chain_info() -> EvmChainInfo {
1132 EvmChainInfo {
1133 id: 8_453,
1134 label: "Base",
1135 eip155_label: "Base",
1136 address_prefix: "base",
1137 native_currency: default_native_currency(8_453),
1138 is_testnet: false,
1139 color: "#0052FF",
1140 logo: ThemedImage {
1141 light: "https://files.cow.fi/cow-sdk/chains/images/base-logo.svg",
1142 dark: "https://files.cow.fi/cow-sdk/chains/images/base-logo.svg",
1143 },
1144 website: WebUrl { name: "Base", url: "https://base.org" },
1145 docs: WebUrl { name: "Base Docs", url: "https://docs.base.org" },
1146 block_explorer: WebUrl { name: "Basescan", url: "https://basescan.org" },
1147 bridges: &[WebUrl { name: "Superchain Bridges", url: "https://bridge.base.org/deposit" }],
1148 contracts: multicall3_only(5022),
1149 rpc_urls: ChainRpcUrls { http: &["https://mainnet.base.org"], web_socket: None },
1150 is_zk_sync: false,
1151 is_under_development: false,
1152 is_deprecated: false,
1153 }
1154}
1155
1156const fn sepolia_chain_info() -> EvmChainInfo {
1157 EvmChainInfo {
1158 id: 11_155_111,
1159 label: "Sepolia",
1160 eip155_label: "Ethereum Sepolia",
1161 address_prefix: "sep",
1162 native_currency: default_native_currency(11_155_111),
1163 is_testnet: true,
1164 color: "#C12FF2",
1165 logo: ThemedImage {
1166 light: "https://files.cow.fi/cow-sdk/chains/images/sepolia-logo.svg",
1167 dark: "https://files.cow.fi/cow-sdk/chains/images/sepolia-logo.svg",
1168 },
1169 website: WebUrl { name: "Ethereum", url: "https://sepolia.dev" },
1170 docs: WebUrl {
1171 name: "Sepolia Docs",
1172 url: "https://ethereum.org/en/developers/docs/networks/#sepolia",
1173 },
1174 block_explorer: WebUrl { name: "Etherscan", url: "https://sepolia.etherscan.io" },
1175 bridges: &[],
1176 contracts: ChainContracts {
1177 multicall3: Some(ChainContract { address: MULTICALL3, block_created: Some(751_532) }),
1178 ens_registry: Some(ChainContract {
1179 address: Address::new([
1180 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x2e, 0x07, 0x4e, 0xc6, 0x9a, 0x0d, 0xfb,
1181 0x29, 0x97, 0xba, 0x6c, 0x7d, 0x2e, 0x1e,
1182 ]),
1183 block_created: None,
1184 }),
1185 ens_universal_resolver: Some(ChainContract {
1186 address: Address::new([
1187 0xc8, 0xaf, 0x99, 0x9e, 0x38, 0x27, 0x3d, 0x65, 0x8b, 0xe1, 0xb9, 0x21, 0xb8,
1188 0x8a, 0x9d, 0xdf, 0x00, 0x57, 0x69, 0xcc,
1189 ]),
1190 block_created: Some(5_317_080),
1191 }),
1192 },
1193 rpc_urls: ChainRpcUrls { http: &["https://sepolia.drpc.org"], web_socket: None },
1194 is_zk_sync: false,
1195 is_under_development: false,
1196 is_deprecated: false,
1197 }
1198}
1199
1200const fn polygon_chain_info() -> EvmChainInfo {
1201 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/polygon-logo.svg";
1202 EvmChainInfo {
1203 id: 137,
1204 label: "Polygon",
1205 eip155_label: "Polygon Mainnet",
1206 address_prefix: "matic",
1207 native_currency: ChainTokenInfo {
1208 chain_id: 137,
1209 address: EVM_NATIVE_ADDR,
1210 decimals: 18,
1211 name: "POL",
1212 symbol: "POL",
1213 logo_url: Some(logo_url),
1214 },
1215 is_testnet: false,
1216 color: "#8247e5",
1217 logo: ThemedImage { light: logo_url, dark: logo_url },
1218 website: WebUrl { name: "Polygon", url: "https://polygon.technology" },
1219 docs: WebUrl { name: "Polygon Docs", url: "https://docs.polygon.technology" },
1220 block_explorer: WebUrl { name: "Polygonscan", url: "https://polygonscan.com" },
1221 bridges: &[],
1222 contracts: multicall3_only(25_770_160),
1223 rpc_urls: ChainRpcUrls { http: &["https://polygon-rpc.com"], web_socket: None },
1224 is_zk_sync: false,
1225 is_under_development: false,
1226 is_deprecated: false,
1227 }
1228}
1229
1230const fn avalanche_chain_info() -> EvmChainInfo {
1231 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/avax-logo.svg";
1232 EvmChainInfo {
1233 id: 43_114,
1234 label: "Avalanche",
1235 eip155_label: "Avalanche C-Chain",
1236 address_prefix: "avax",
1237 native_currency: ChainTokenInfo {
1238 chain_id: 43_114,
1239 address: EVM_NATIVE_ADDR,
1240 decimals: 18,
1241 name: "Avalanche",
1242 symbol: "AVAX",
1243 logo_url: Some(logo_url),
1244 },
1245 is_testnet: false,
1246 color: "#ff3944",
1247 logo: ThemedImage { light: logo_url, dark: logo_url },
1248 website: WebUrl { name: "Avalanche", url: "https://www.avax.network/" },
1249 docs: WebUrl { name: "Avalanche Docs", url: "https://build.avax.network/docs" },
1250 block_explorer: WebUrl { name: "Snowscan", url: "https://snowscan.xyz" },
1251 bridges: &[],
1252 contracts: multicall3_only(11_907_934),
1253 rpc_urls: ChainRpcUrls {
1254 http: &["https://api.avax.network/ext/bc/C/rpc"],
1255 web_socket: None,
1256 },
1257 is_zk_sync: false,
1258 is_under_development: false,
1259 is_deprecated: false,
1260 }
1261}
1262
1263const fn bnb_chain_info() -> EvmChainInfo {
1264 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/bnb-logo.svg";
1265 EvmChainInfo {
1266 id: 56,
1267 label: "BNB",
1268 eip155_label: "BNB Chain Mainnet",
1269 address_prefix: "bnb",
1270 native_currency: ChainTokenInfo {
1271 chain_id: 56,
1272 address: EVM_NATIVE_ADDR,
1273 decimals: 18,
1274 name: "BNB Chain Native Token",
1275 symbol: "BNB",
1276 logo_url: Some(logo_url),
1277 },
1278 is_testnet: false,
1279 color: "#F0B90B",
1280 logo: ThemedImage { light: logo_url, dark: logo_url },
1281 website: WebUrl { name: "BNB Chain", url: "https://www.bnbchain.org" },
1282 docs: WebUrl { name: "BNB Chain Docs", url: "https://docs.bnbchain.org" },
1283 block_explorer: WebUrl { name: "Bscscan", url: "https://bscscan.com" },
1284 bridges: &[WebUrl {
1285 name: "BNB Chain Cross-Chain Bridge",
1286 url: "https://www.bnbchain.org/en/bnb-chain-bridge",
1287 }],
1288 contracts: multicall3_only(15_921_452),
1289 rpc_urls: ChainRpcUrls { http: &["https://bsc-dataseed1.bnbchain.org"], web_socket: None },
1290 is_zk_sync: false,
1291 is_under_development: false,
1292 is_deprecated: false,
1293 }
1294}
1295
1296const fn linea_chain_info() -> EvmChainInfo {
1297 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/linea-logo.svg";
1298 EvmChainInfo {
1299 id: 59_144,
1300 label: "Linea",
1301 eip155_label: "Linea Mainnet",
1302 address_prefix: "linea",
1303 native_currency: default_native_currency(59_144),
1304 is_testnet: false,
1305 color: "#61dfff",
1306 logo: ThemedImage { light: logo_url, dark: logo_url },
1307 website: WebUrl { name: "Linea", url: "https://linea.build" },
1308 docs: WebUrl { name: "Linea Docs", url: "https://docs.linea.build" },
1309 block_explorer: WebUrl { name: "Lineascan", url: "https://lineascan.build" },
1310 bridges: &[WebUrl { name: "Linea Bridge", url: "https://linea.build/hub/bridge" }],
1311 contracts: multicall3_only(42),
1312 rpc_urls: ChainRpcUrls { http: &["https://rpc.linea.build"], web_socket: None },
1313 is_zk_sync: false,
1314 is_under_development: false,
1315 is_deprecated: false,
1316 }
1317}
1318
1319const fn lens_chain_info() -> EvmChainInfo {
1321 EvmChainInfo {
1322 id: 232,
1323 label: "Lens",
1324 eip155_label: "Lens Network",
1325 address_prefix: "lens",
1326 native_currency: ChainTokenInfo {
1327 chain_id: 232,
1328 address: EVM_NATIVE_ADDR,
1329 decimals: 18,
1330 name: "GHO",
1331 symbol: "GHO",
1332 logo_url: None,
1333 },
1334 is_testnet: false,
1335 color: "#00501e",
1336 logo: ThemedImage {
1337 light: "https://files.cow.fi/cow-sdk/chains/images/lens-logo.svg",
1338 dark: "https://files.cow.fi/cow-sdk/chains/images/lens-logo.svg",
1339 },
1340 website: WebUrl { name: "Lens", url: "https://lens.xyz" },
1341 docs: WebUrl { name: "Lens Docs", url: "https://docs.lens.xyz" },
1342 block_explorer: WebUrl { name: "Lens Explorer", url: "https://explorer.lens.xyz" },
1343 bridges: &[],
1344 contracts: no_contracts(),
1345 rpc_urls: ChainRpcUrls { http: &["https://rpc.lens.xyz"], web_socket: None },
1346 is_zk_sync: true,
1347 is_under_development: false,
1348 is_deprecated: false,
1349 }
1350}
1351
1352const fn plasma_chain_info() -> EvmChainInfo {
1353 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/plasma-logo.svg";
1354 EvmChainInfo {
1355 id: 9_745,
1356 label: "Plasma",
1357 eip155_label: "Plasma Mainnet",
1358 address_prefix: "plasma",
1359 native_currency: ChainTokenInfo {
1360 chain_id: 9_745,
1361 address: EVM_NATIVE_ADDR,
1362 decimals: 18,
1363 name: "Plasma",
1364 symbol: "XPL",
1365 logo_url: Some(logo_url),
1366 },
1367 is_testnet: false,
1368 color: "#569F8C",
1369 logo: ThemedImage { light: logo_url, dark: logo_url },
1370 website: WebUrl { name: "Plasma", url: "https://www.plasma.to" },
1371 docs: WebUrl { name: "Plasma Docs", url: "https://docs.plasma.to" },
1372 block_explorer: WebUrl { name: "Plasmascan", url: "https://plasmascan.to" },
1373 bridges: &[],
1374 contracts: multicall3_only(0),
1375 rpc_urls: ChainRpcUrls { http: &["https://rpc.plasma.to"], web_socket: None },
1376 is_zk_sync: false,
1377 is_under_development: false,
1378 is_deprecated: false,
1379 }
1380}
1381
1382const fn ink_chain_info() -> EvmChainInfo {
1383 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/ink-logo.svg";
1384 EvmChainInfo {
1385 id: 57_073,
1386 label: "Ink",
1387 eip155_label: "Ink Chain Mainnet",
1388 address_prefix: "ink",
1389 native_currency: default_native_currency(57_073),
1390 is_testnet: false,
1391 color: "#7132f5",
1392 logo: ThemedImage { light: logo_url, dark: logo_url },
1393 website: WebUrl { name: "Ink", url: "https://inkonchain.com/" },
1394 docs: WebUrl { name: "Ink Docs", url: "https://docs.inkonchain.com" },
1395 block_explorer: WebUrl { name: "Ink Explorer", url: "https://explorer.inkonchain.com" },
1396 bridges: &[WebUrl { name: "Ink Bridge", url: "https://inkonchain.com/bridge" }],
1397 contracts: multicall3_only(0),
1398 rpc_urls: ChainRpcUrls { http: &["https://rpc-ten.inkonchain.com"], web_socket: None },
1399 is_zk_sync: false,
1400 is_under_development: false,
1401 is_deprecated: false,
1402 }
1403}
1404
1405const fn optimism_chain_info() -> EvmChainInfo {
1406 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/optimism-logo.svg";
1407 EvmChainInfo {
1408 id: 10,
1409 label: "Optimism",
1410 eip155_label: "OP Mainnet",
1411 address_prefix: "op",
1412 native_currency: default_native_currency(10),
1413 is_testnet: false,
1414 color: "#ff0420",
1415 logo: ThemedImage { light: logo_url, dark: logo_url },
1416 website: WebUrl { name: "Optimism", url: "https://optimism.io" },
1417 docs: WebUrl { name: "Optimism Docs", url: "https://docs.optimism.io" },
1418 block_explorer: WebUrl { name: "Etherscan", url: "https://optimistic.etherscan.io" },
1419 bridges: &[],
1420 contracts: multicall3_only(4_286_263),
1421 rpc_urls: ChainRpcUrls { http: &["https://mainnet.optimism.io"], web_socket: None },
1422 is_zk_sync: false,
1423 is_under_development: false,
1424 is_deprecated: false,
1425 }
1426}
1427
1428const fn bitcoin_chain_info() -> NonEvmChainInfo {
1429 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/bitcoin-logo.svg";
1430 NonEvmChainInfo {
1431 id: 1_000_000_000,
1432 label: "Bitcoin",
1433 address_prefix: "btc",
1434 native_currency: ChainTokenInfo {
1435 chain_id: 1_000_000_000,
1436 address: BTC_ADDR,
1437 decimals: 8,
1438 name: "Bitcoin",
1439 symbol: "BTC",
1440 logo_url: Some(logo_url),
1441 },
1442 is_testnet: false,
1443 color: "#f7931a",
1444 logo: ThemedImage { light: logo_url, dark: logo_url },
1445 website: WebUrl { name: "Bitcoin", url: "https://bitcoin.org" },
1446 docs: WebUrl {
1447 name: "Bitcoin Docs",
1448 url: "https://bitcoin.org/en/developer-documentation",
1449 },
1450 block_explorer: WebUrl { name: "Blockstream Explorer", url: "https://blockstream.info" },
1451 is_under_development: false,
1452 is_deprecated: false,
1453 }
1454}
1455
1456const fn solana_chain_info() -> NonEvmChainInfo {
1457 let logo_url = "https://files.cow.fi/cow-sdk/chains/images/solana-logo.svg";
1458 NonEvmChainInfo {
1459 id: 1_000_000_001,
1460 label: "Solana",
1461 address_prefix: "sol",
1462 native_currency: ChainTokenInfo {
1463 chain_id: 1_000_000_001,
1464 address: SOL_ADDR,
1465 decimals: 9,
1466 name: "Solana",
1467 symbol: "SOL",
1468 logo_url: Some(logo_url),
1469 },
1470 is_testnet: false,
1471 color: "#9945FF",
1472 logo: ThemedImage { light: logo_url, dark: logo_url },
1473 website: WebUrl { name: "Solana", url: "https://solana.com" },
1474 docs: WebUrl { name: "Solana Docs", url: "https://docs.solana.com" },
1475 block_explorer: WebUrl { name: "Solana Explorer", url: "https://explorer.solana.com" },
1476 is_under_development: false,
1477 is_deprecated: false,
1478 }
1479}
1480
1481#[derive(Debug, Clone, Default)]
1485pub struct IpfsConfig {
1486 pub uri: Option<String>,
1488 pub write_uri: Option<String>,
1490 pub read_uri: Option<String>,
1492 pub pinata_api_key: Option<String>,
1494 pub pinata_api_secret: Option<String>,
1496}
1497
1498#[derive(Debug, Clone)]
1503pub struct ApiContext {
1504 pub chain_id: SupportedChainId,
1506 pub env: super::chain::Env,
1508 pub base_urls: Option<ApiBaseUrls>,
1510 pub api_key: Option<String>,
1512}
1513
1514impl Default for ApiContext {
1515 fn default() -> Self {
1517 Self {
1518 chain_id: SupportedChainId::Mainnet,
1519 env: super::chain::Env::Prod,
1520 base_urls: None,
1521 api_key: None,
1522 }
1523 }
1524}
1525
1526#[derive(Debug, Clone, Default)]
1529pub struct ProtocolOptions {
1530 pub env: Option<super::chain::Env>,
1532 pub settlement_contract_override: Option<AddressPerChain>,
1534 pub eth_flow_contract_override: Option<AddressPerChain>,
1536}
1537
1538#[derive(Debug, Clone)]
1540pub struct EvmCall {
1541 pub to: Address,
1543 pub data: Vec<u8>,
1545 pub value: U256,
1547}
1548
1549#[cfg(test)]
1550mod tests {
1551 use super::*;
1552
1553 #[test]
1556 fn evm_chains_roundtrip_u64() {
1557 let chains = [
1558 (EvmChains::Mainnet, 1),
1559 (EvmChains::Optimism, 10),
1560 (EvmChains::Bnb, 56),
1561 (EvmChains::GnosisChain, 100),
1562 (EvmChains::Polygon, 137),
1563 (EvmChains::Base, 8_453),
1564 (EvmChains::Plasma, 9_745),
1565 (EvmChains::ArbitrumOne, 42_161),
1566 (EvmChains::Avalanche, 43_114),
1567 (EvmChains::Ink, 57_073),
1568 (EvmChains::Linea, 59_144),
1569 (EvmChains::Sepolia, 11_155_111),
1570 ];
1571 for (chain, id) in chains {
1572 assert_eq!(chain.as_u64(), id);
1573 assert_eq!(EvmChains::try_from_u64(id), Some(chain));
1574 }
1575 }
1576
1577 #[test]
1578 fn evm_chains_unknown_returns_none() {
1579 assert_eq!(EvmChains::try_from_u64(9999), None);
1580 }
1581
1582 #[test]
1585 fn non_evm_chains_roundtrip() {
1586 assert_eq!(NonEvmChains::Bitcoin.as_u64(), 1_000_000_000);
1587 assert_eq!(NonEvmChains::Solana.as_u64(), 1_000_000_001);
1588 assert_eq!(NonEvmChains::try_from_u64(1_000_000_000), Some(NonEvmChains::Bitcoin));
1589 assert_eq!(NonEvmChains::try_from_u64(1_000_000_001), Some(NonEvmChains::Solana));
1590 assert_eq!(NonEvmChains::try_from_u64(999), None);
1591 }
1592
1593 #[test]
1596 fn additional_target_roundtrip() {
1597 for &chain in AdditionalTargetChainId::all() {
1598 let id = chain.as_u64();
1599 assert_eq!(AdditionalTargetChainId::try_from_u64(id), Some(chain));
1600 }
1601 }
1602
1603 #[test]
1604 fn additional_target_all_has_three() {
1605 assert_eq!(AdditionalTargetChainId::all().len(), 3);
1606 }
1607
1608 #[test]
1611 fn target_chain_id_as_u64() {
1612 let supported = TargetChainId::Supported(SupportedChainId::Mainnet);
1613 assert_eq!(supported.as_u64(), 1);
1614 let additional = TargetChainId::Additional(AdditionalTargetChainId::Bitcoin);
1615 assert_eq!(additional.as_u64(), 1_000_000_000);
1616 }
1617
1618 #[test]
1621 fn is_evm_chain_correct() {
1622 assert!(is_evm_chain(1));
1623 assert!(is_evm_chain(10));
1624 assert!(!is_evm_chain(1_000_000_000));
1625 assert!(!is_evm_chain(9999));
1626 }
1627
1628 #[test]
1629 fn is_non_evm_chain_correct() {
1630 assert!(is_non_evm_chain(1_000_000_000));
1631 assert!(is_non_evm_chain(1_000_000_001));
1632 assert!(!is_non_evm_chain(1));
1633 }
1634
1635 #[test]
1636 fn is_btc_chain_correct() {
1637 assert!(is_btc_chain(1_000_000_000));
1638 assert!(!is_btc_chain(1));
1639 }
1640
1641 #[test]
1642 fn is_supported_chain_correct() {
1643 assert!(is_supported_chain(1));
1644 assert!(is_supported_chain(100));
1645 assert!(!is_supported_chain(10));
1646 assert!(!is_supported_chain(9999));
1647 }
1648
1649 #[test]
1650 fn is_additional_target_chain_correct() {
1651 assert!(is_additional_target_chain(10));
1652 assert!(is_additional_target_chain(1_000_000_000));
1653 assert!(!is_additional_target_chain(1));
1654 }
1655
1656 #[test]
1657 fn is_target_chain_id_correct() {
1658 assert!(is_target_chain_id(1));
1659 assert!(is_target_chain_id(10));
1660 assert!(is_target_chain_id(1_000_000_000));
1661 assert!(!is_target_chain_id(9999));
1662 }
1663
1664 #[test]
1665 fn is_zk_sync_chain_is_false_for_all() {
1666 assert!(!is_zk_sync_chain(1));
1667 assert!(!is_zk_sync_chain(100));
1668 }
1669
1670 #[test]
1673 fn get_chain_info_all_supported() {
1674 for &chain in SupportedChainId::all() {
1675 let chain_info = get_chain_info(chain.as_u64());
1676 assert!(chain_info.is_some(), "no chain info for {chain:?}");
1677 let info = chain_info.unwrap_or_else(|| supported_chain_info(chain));
1678 assert_eq!(info.id(), chain.as_u64());
1679 assert!(!info.label().is_empty());
1680 }
1681 }
1682
1683 #[test]
1684 fn get_chain_info_additional_targets() {
1685 for &chain in AdditionalTargetChainId::all() {
1686 let info = get_chain_info(chain.as_u64());
1687 assert!(info.is_some(), "no chain info for {chain:?}");
1688 }
1689 }
1690
1691 #[test]
1692 fn get_chain_info_unknown_returns_none() {
1693 assert!(get_chain_info(9999).is_none());
1694 }
1695
1696 #[test]
1697 fn chain_info_evm_predicates() {
1698 let info = supported_chain_info(SupportedChainId::Mainnet);
1699 assert!(info.is_evm());
1700 assert!(!info.is_non_evm());
1701 assert!(info.as_evm().is_some());
1702 assert!(info.as_non_evm().is_none());
1703 }
1704
1705 #[test]
1706 fn chain_info_non_evm_predicates() {
1707 let info = additional_target_chain_info(AdditionalTargetChainId::Bitcoin);
1708 assert!(!info.is_evm());
1709 assert!(info.is_non_evm());
1710 assert!(info.as_non_evm().is_some());
1711 }
1712
1713 #[test]
1714 fn chain_info_native_currency() {
1715 let info = supported_chain_info(SupportedChainId::Mainnet);
1716 let currency = info.native_currency();
1717 assert_eq!(currency.decimals, 18);
1718 }
1719
1720 #[test]
1723 fn all_supported_chain_ids_matches_all() {
1724 let ids = all_supported_chain_ids();
1725 assert_eq!(ids.len(), SupportedChainId::all().len());
1726 }
1727
1728 #[test]
1729 fn all_supported_chains_matches_all() {
1730 let chains = all_supported_chains();
1731 assert_eq!(chains.len(), SupportedChainId::all().len());
1732 }
1733
1734 #[test]
1735 fn tradable_chains_excludes_deprecated_and_dev() {
1736 let tradable = tradable_supported_chain_ids();
1737 assert!(!tradable.is_empty());
1738 assert!(tradable.len() <= SupportedChainId::all().len());
1739 }
1740
1741 #[test]
1742 fn all_additional_target_chain_ids_has_three() {
1743 assert_eq!(all_additional_target_chain_ids().len(), 3);
1744 }
1745
1746 #[test]
1747 fn all_chains_includes_supported_and_additional() {
1748 let all = all_chains();
1749 assert!(all.len() >= SupportedChainId::all().len());
1750 }
1751
1752 #[test]
1753 fn all_chain_ids_includes_both() {
1754 let ids = all_chain_ids();
1755 assert!(ids.len() >= SupportedChainId::all().len() + AdditionalTargetChainId::all().len());
1756 }
1757
1758 #[test]
1759 fn map_supported_networks_maps_all() {
1760 let mapped = map_supported_networks(|c| c.as_u64());
1761 assert_eq!(mapped.len(), SupportedChainId::all().len());
1762 }
1763
1764 #[test]
1765 fn map_address_to_supported_networks_produces_correct_count() {
1766 let mapped = map_address_to_supported_networks(Address::ZERO);
1767 assert_eq!(mapped.len(), SupportedChainId::all().len());
1768 }
1769
1770 #[test]
1773 fn is_zk_sync_chain_returns_false_for_lens() {
1774 assert!(!is_zk_sync_chain(232));
1777 }
1778
1779 #[test]
1780 fn is_zk_sync_chain_returns_false_for_non_evm() {
1781 assert!(!is_zk_sync_chain(1_000_000_000));
1782 }
1783
1784 #[test]
1785 fn is_chain_under_development_unknown_chain() {
1786 assert!(!is_chain_under_development(9999));
1787 }
1788
1789 #[test]
1790 fn is_chain_deprecated_unknown_chain() {
1791 assert!(!is_chain_deprecated(9999));
1792 }
1793
1794 #[test]
1795 fn chain_info_is_under_development_non_evm() {
1796 let info = additional_target_chain_info(AdditionalTargetChainId::Bitcoin);
1797 assert!(!info.is_under_development());
1798 }
1799
1800 #[test]
1801 fn chain_info_is_deprecated_non_evm() {
1802 let info = additional_target_chain_info(AdditionalTargetChainId::Bitcoin);
1803 assert!(!info.is_deprecated());
1804 }
1805
1806 #[test]
1807 fn evm_chain_info_type_guards() {
1808 let info = supported_chain_info(SupportedChainId::Mainnet);
1809 assert!(is_evm_chain_info(&info));
1810 assert!(!is_non_evm_chain_info(&info));
1811 }
1812
1813 #[test]
1814 fn non_evm_chain_info_type_guards() {
1815 let info = additional_target_chain_info(AdditionalTargetChainId::Bitcoin);
1816 assert!(is_non_evm_chain_info(&info));
1817 assert!(!is_evm_chain_info(&info));
1818 }
1819
1820 #[test]
1821 fn tradable_supported_chains_returns_chain_infos() {
1822 let chains = tradable_supported_chains();
1823 assert!(!chains.is_empty());
1824 for c in &chains {
1825 assert!(!c.is_deprecated());
1826 }
1827 }
1828
1829 #[test]
1830 fn all_additional_target_chains_returns_infos() {
1831 let chains = all_additional_target_chains();
1832 assert_eq!(chains.len(), 3);
1833 }
1834
1835 #[test]
1836 fn map_all_networks_covers_everything() {
1837 let mapped = map_all_networks(|t| t.as_u64());
1838 assert!(
1839 mapped.len() >= SupportedChainId::all().len() + AdditionalTargetChainId::all().len()
1840 );
1841 }
1842
1843 #[test]
1844 fn chain_info_label_non_evm() {
1845 let info = additional_target_chain_info(AdditionalTargetChainId::Solana);
1846 assert_eq!(info.label(), "Solana");
1847 assert_eq!(info.id(), 1_000_000_001);
1848 }
1849
1850 #[test]
1851 fn additional_target_optimism_is_evm() {
1852 let info = additional_target_chain_info(AdditionalTargetChainId::Optimism);
1853 assert!(info.is_evm());
1854 assert_eq!(info.label(), "Optimism");
1855 }
1856
1857 #[test]
1858 fn api_context_default() {
1859 let ctx = ApiContext::default();
1860 assert_eq!(ctx.chain_id, SupportedChainId::Mainnet);
1861 assert!(ctx.base_urls.is_none());
1862 assert!(ctx.api_key.is_none());
1863 }
1864
1865 #[test]
1866 fn additional_target_try_from_u64_unknown() {
1867 assert!(AdditionalTargetChainId::try_from_u64(999).is_none());
1868 }
1869}