use core::{
fmt::{Debug, Display},
str::FromStr,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use super::utils::hex_string_to_uint;
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub enum ChainId {
EthereumMainnet,
EthereumSepolia,
StarknetMainnet,
StarknetSepolia,
}
#[derive(Error, Debug, PartialEq)]
#[error("Failed to parse ChainId: {input}")]
pub struct ParseChainIdError {
input: String,
}
impl TryFrom<u128> for ChainId {
type Error = ParseChainIdError;
fn try_from(value: u128) -> Result<Self, Self::Error> {
Self::from_numeric_id(value)
}
}
impl From<ChainId> for u128 {
fn from(chain_id: ChainId) -> Self {
chain_id.to_numeric_id()
}
}
impl<'de> Deserialize<'de> for ChainId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
ChainId::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl Serialize for ChainId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl FromStr for ChainId {
type Err = ParseChainIdError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ETHEREUM_MAINNET" => Ok(Self::EthereumMainnet),
"ETHEREUM_SEPOLIA" => Ok(Self::EthereumSepolia),
"STARKNET_MAINNET" => Ok(Self::StarknetMainnet),
"STARKNET_SEPOLIA" => Ok(Self::StarknetSepolia),
_ => Err(ParseChainIdError {
input: s.to_string(),
}),
}
}
}
impl Display for ChainId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ChainId::EthereumMainnet => write!(f, "ETHEREUM_MAINNET"),
ChainId::EthereumSepolia => write!(f, "ETHEREUM_SEPOLIA"),
ChainId::StarknetMainnet => write!(f, "STARKNET_MAINNET"),
ChainId::StarknetSepolia => write!(f, "STARKNET_SEPOLIA"),
}
}
}
impl Debug for ChainId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ChainId::EthereumMainnet => write!(f, "ETHEREUM_MAINNET"),
ChainId::EthereumSepolia => write!(f, "ETHEREUM_SEPOLIA"),
ChainId::StarknetMainnet => write!(f, "STARKNET_MAINNET"),
ChainId::StarknetSepolia => write!(f, "STARKNET_SEPOLIA"),
}
}
}
impl ChainId {
pub fn to_numeric_id(&self) -> u128 {
match self {
ChainId::EthereumMainnet => 1,
ChainId::EthereumSepolia => 11155111,
ChainId::StarknetMainnet => 23448594291968334,
ChainId::StarknetSepolia => 393402133025997798000961,
}
}
pub fn from_numeric_id(id: u128) -> Result<Self, ParseChainIdError> {
match id {
1 => Ok(Self::EthereumMainnet),
11155111 => Ok(Self::EthereumSepolia),
23448594291968334 => Ok(Self::StarknetMainnet),
393402133025997798000961 => Ok(Self::StarknetSepolia),
i => Err(ParseChainIdError {
input: i.to_string(),
}),
}
}
pub fn from_hex_str(s: &str) -> Result<Self, ParseChainIdError> {
ChainId::from_numeric_id(hex_string_to_uint(s))
}
pub fn to_be_bytes(&self) -> [u8; 16] {
self.to_numeric_id().to_be_bytes()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_str() {
assert_eq!(
ChainId::from_str("ETHEREUM_MAINNET").unwrap(),
ChainId::EthereumMainnet
);
assert_eq!(
ChainId::from_str("ETHEREUM_SEPOLIA").unwrap(),
ChainId::EthereumSepolia
);
assert_eq!(
ChainId::from_str("STARKNET_MAINNET").unwrap(),
ChainId::StarknetMainnet
);
assert_eq!(
ChainId::from_str("STARKNET_SEPOLIA").unwrap(),
ChainId::StarknetSepolia
);
assert!(ChainId::from_str("INVALID").is_err());
}
#[test]
fn test_display() {
assert_eq!(ChainId::EthereumMainnet.to_string(), "ETHEREUM_MAINNET");
assert_eq!(ChainId::EthereumSepolia.to_string(), "ETHEREUM_SEPOLIA");
assert_eq!(ChainId::StarknetMainnet.to_string(), "STARKNET_MAINNET");
assert_eq!(ChainId::StarknetSepolia.to_string(), "STARKNET_SEPOLIA");
}
#[test]
fn test_to_numeric_id() {
assert_eq!(ChainId::EthereumMainnet.to_numeric_id(), 1);
assert_eq!(ChainId::EthereumSepolia.to_numeric_id(), 11155111);
assert_eq!(ChainId::StarknetMainnet.to_numeric_id(), 23448594291968334);
assert_eq!(
ChainId::StarknetSepolia.to_numeric_id(),
393402133025997798000961
);
}
#[test]
fn test_from_numeric_id() {
assert_eq!(ChainId::from_numeric_id(1), Ok(ChainId::EthereumMainnet));
assert_eq!(
ChainId::from_numeric_id(11155111),
Ok(ChainId::EthereumSepolia)
);
assert_eq!(
ChainId::from_numeric_id(23448594291968334),
Ok(ChainId::StarknetMainnet)
);
assert_eq!(
ChainId::from_numeric_id(393402133025997798000961),
Ok(ChainId::StarknetSepolia)
);
assert!(ChainId::from_numeric_id(999).is_err());
}
}