use alloy_chains::NamedChain;
use alloy_primitives::Address;
use super::addresses::{
CCTP_V2_MESSAGE_TRANSMITTER_MAINNET, CCTP_V2_MESSAGE_TRANSMITTER_TESTNET,
CCTP_V2_TOKEN_MESSENGER_MAINNET, CCTP_V2_TOKEN_MESSENGER_TESTNET,
};
use crate::{CctpError, DomainId, Result};
pub trait CctpV2 {
fn supports_cctp_v2(&self) -> bool;
fn supports_fast_transfer(&self) -> Result<bool>;
fn fast_transfer_fee_bps(&self) -> Result<Option<u32>>;
fn token_messenger_v2_address(&self) -> Result<Address>;
fn message_transmitter_v2_address(&self) -> Result<Address>;
fn cctp_v2_domain_id(&self) -> Result<DomainId>;
fn fast_transfer_confirmation_time_seconds(&self) -> Result<u64>;
fn standard_transfer_confirmation_time_seconds(&self) -> Result<u64>;
}
impl CctpV2 for NamedChain {
fn supports_cctp_v2(&self) -> bool {
matches!(
self,
Self::Mainnet
| Self::Sepolia
| Self::Arbitrum
| Self::ArbitrumSepolia
| Self::Base
| Self::BaseSepolia
| Self::Optimism
| Self::OptimismSepolia
| Self::Avalanche
| Self::AvalancheFuji
| Self::Polygon
| Self::PolygonAmoy
| Self::Unichain
| Self::Linea
| Self::Sonic
| Self::Sei
)
}
fn supports_fast_transfer(&self) -> Result<bool> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(true)
}
fn fast_transfer_fee_bps(&self) -> Result<Option<u32>> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(Some(0))
}
fn token_messenger_v2_address(&self) -> Result<Address> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(if self.is_testnet() {
CCTP_V2_TOKEN_MESSENGER_TESTNET
} else {
CCTP_V2_TOKEN_MESSENGER_MAINNET
})
}
fn message_transmitter_v2_address(&self) -> Result<Address> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(if self.is_testnet() {
CCTP_V2_MESSAGE_TRANSMITTER_TESTNET
} else {
CCTP_V2_MESSAGE_TRANSMITTER_MAINNET
})
}
fn cctp_v2_domain_id(&self) -> Result<DomainId> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(match self {
Self::Mainnet | Self::Sepolia => DomainId::Ethereum,
Self::Avalanche | Self::AvalancheFuji => DomainId::Avalanche,
Self::Optimism | Self::OptimismSepolia => DomainId::Optimism,
Self::Arbitrum | Self::ArbitrumSepolia => DomainId::Arbitrum,
Self::Base | Self::BaseSepolia => DomainId::Base,
Self::Polygon | Self::PolygonAmoy => DomainId::Polygon,
Self::Unichain => DomainId::Unichain,
Self::Linea => DomainId::Linea,
Self::Sonic => DomainId::Sonic,
Self::Sei => DomainId::Sei,
_ => return Err(CctpError::UnsupportedChain(*self)),
})
}
fn fast_transfer_confirmation_time_seconds(&self) -> Result<u64> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(match self {
Self::Mainnet | Self::Sepolia => 20,
Self::Arbitrum | Self::ArbitrumSepolia => 8,
Self::Base | Self::BaseSepolia => 8,
Self::Optimism | Self::OptimismSepolia => 8,
Self::Avalanche | Self::AvalancheFuji => 8,
Self::Polygon | Self::PolygonAmoy => 8,
Self::Unichain => 8,
Self::Linea => 8,
Self::Sonic => 5,
Self::Sei => 5,
_ => return Err(CctpError::UnsupportedChain(*self)),
})
}
fn standard_transfer_confirmation_time_seconds(&self) -> Result<u64> {
if !self.supports_cctp_v2() {
return Err(CctpError::UnsupportedChain(*self));
}
Ok(match self {
Self::Mainnet | Self::Sepolia => 19 * 60,
Self::Arbitrum | Self::ArbitrumSepolia => 19 * 60,
Self::Base | Self::BaseSepolia => 19 * 60,
Self::Optimism | Self::OptimismSepolia => 19 * 60,
Self::Unichain => 19 * 60,
Self::Avalanche | Self::AvalancheFuji => 20,
Self::Polygon | Self::PolygonAmoy => 8 * 60,
Self::Linea => 8 * 60 * 60,
Self::Sonic => 5,
Self::Sei => 5,
_ => return Err(CctpError::UnsupportedChain(*self)),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_v2_chain_support() {
assert!(NamedChain::Mainnet.supports_cctp_v2());
assert!(NamedChain::Arbitrum.supports_cctp_v2());
assert!(NamedChain::Base.supports_cctp_v2());
assert!(NamedChain::Linea.supports_cctp_v2());
assert!(NamedChain::Sonic.supports_cctp_v2());
assert!(NamedChain::Sei.supports_cctp_v2());
assert!(!NamedChain::Moonbeam.supports_cctp_v2());
}
#[test]
fn test_fast_transfer_support() {
assert!(NamedChain::Mainnet.supports_fast_transfer().unwrap());
assert!(NamedChain::Linea.supports_fast_transfer().unwrap());
assert!(NamedChain::Sonic.supports_fast_transfer().unwrap());
assert!(NamedChain::Moonbeam.supports_fast_transfer().is_err());
}
#[test]
fn test_fast_transfer_fees() {
assert_eq!(
NamedChain::Mainnet.fast_transfer_fee_bps().unwrap(),
Some(0)
);
assert_eq!(NamedChain::Linea.fast_transfer_fee_bps().unwrap(), Some(0));
}
#[test]
fn test_domain_id_mapping() {
assert_eq!(
NamedChain::Mainnet.cctp_v2_domain_id().unwrap(),
DomainId::Ethereum
);
assert_eq!(
NamedChain::Arbitrum.cctp_v2_domain_id().unwrap(),
DomainId::Arbitrum
);
assert_eq!(
NamedChain::Linea.cctp_v2_domain_id().unwrap(),
DomainId::Linea
);
assert_eq!(
NamedChain::Sonic.cctp_v2_domain_id().unwrap(),
DomainId::Sonic
);
assert_eq!(NamedChain::Sei.cctp_v2_domain_id().unwrap(), DomainId::Sei);
}
#[test]
fn test_contract_addresses() {
let linea_tm = NamedChain::Linea.token_messenger_v2_address().unwrap();
let linea_mt = NamedChain::Linea.message_transmitter_v2_address().unwrap();
assert_eq!(linea_tm, CCTP_V2_TOKEN_MESSENGER_MAINNET);
assert_eq!(linea_mt, CCTP_V2_MESSAGE_TRANSMITTER_MAINNET);
let sonic_tm = NamedChain::Sonic.token_messenger_v2_address().unwrap();
let sonic_mt = NamedChain::Sonic.message_transmitter_v2_address().unwrap();
assert_eq!(sonic_tm, CCTP_V2_TOKEN_MESSENGER_MAINNET);
assert_eq!(sonic_mt, CCTP_V2_MESSAGE_TRANSMITTER_MAINNET);
assert_eq!(linea_tm, sonic_tm);
assert_eq!(linea_mt, sonic_mt);
}
#[test]
fn test_fast_transfer_confirmation_times() {
assert_eq!(
NamedChain::Mainnet
.fast_transfer_confirmation_time_seconds()
.unwrap(),
20
);
assert_eq!(
NamedChain::Arbitrum
.fast_transfer_confirmation_time_seconds()
.unwrap(),
8
);
assert_eq!(
NamedChain::Linea
.fast_transfer_confirmation_time_seconds()
.unwrap(),
8
);
assert_eq!(
NamedChain::Sonic
.fast_transfer_confirmation_time_seconds()
.unwrap(),
5
);
assert_eq!(
NamedChain::Sei
.fast_transfer_confirmation_time_seconds()
.unwrap(),
5
);
}
#[test]
fn test_standard_transfer_confirmation_times() {
assert_eq!(
NamedChain::Mainnet
.standard_transfer_confirmation_time_seconds()
.unwrap(),
19 * 60
);
assert_eq!(
NamedChain::Arbitrum
.standard_transfer_confirmation_time_seconds()
.unwrap(),
19 * 60
);
assert_eq!(
NamedChain::Base
.standard_transfer_confirmation_time_seconds()
.unwrap(),
19 * 60
);
assert_eq!(
NamedChain::Avalanche
.standard_transfer_confirmation_time_seconds()
.unwrap(),
20
);
assert_eq!(
NamedChain::Polygon
.standard_transfer_confirmation_time_seconds()
.unwrap(),
8 * 60
);
assert_eq!(
NamedChain::Linea
.standard_transfer_confirmation_time_seconds()
.unwrap(),
8 * 60 * 60
);
assert_eq!(
NamedChain::Sonic
.standard_transfer_confirmation_time_seconds()
.unwrap(),
5
);
assert_eq!(
NamedChain::Sei
.standard_transfer_confirmation_time_seconds()
.unwrap(),
5
);
}
}