use std::str::FromStr;
use alloy::primitives::Address;
use anyhow::{anyhow, Context, Result};
use uniswap_sdk_core::addresses::{
CHAIN_TO_ADDRESSES_MAP, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, QUOTER_ADDRESSES,
SWAP_ROUTER_02_ADDRESSES,
};
#[derive(Debug, Clone)]
pub struct PeripheryAddresses {
pub position_manager: Address,
pub quoter: Address,
pub swap_router: Address,
}
#[derive(Debug, Clone)]
pub struct V4PeripheryAddresses {
pub position_manager: Address,
pub pool_manager: Address,
pub state_view: Address,
}
pub fn get_v3_periphery_addresses(chain_id: u64) -> Result<PeripheryAddresses> {
let position_manager =
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES.get(&chain_id).copied().ok_or_else(|| {
anyhow!(
"Uniswap V3 position manager not found for chain_id: {}. Supported chains: {:?}",
chain_id,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES.keys().collect::<Vec<_>>()
)
})?;
let quoter = QUOTER_ADDRESSES.get(&chain_id).copied().ok_or_else(|| {
anyhow!(
"Uniswap V3 quoter not found for chain_id: {}. Supported chains: {:?}",
chain_id,
QUOTER_ADDRESSES.keys().collect::<Vec<_>>()
)
})?;
let swap_router = SWAP_ROUTER_02_ADDRESSES.get(&chain_id).copied().ok_or_else(|| {
anyhow!(
"Uniswap V3 swap router not found for chain_id: {}. Supported chains: {:?}",
chain_id,
SWAP_ROUTER_02_ADDRESSES.keys().collect::<Vec<_>>()
)
})?;
Ok(PeripheryAddresses { position_manager, quoter, swap_router })
}
#[cfg(feature = "pancakeswap")]
pub fn get_pancake_v3_periphery_addresses(chain_id: u64) -> Result<PeripheryAddresses> {
let pancake_addresses =
waterpump_evm_pancakeswap_client::addresses::get_periphery_addresses(chain_id)?;
Ok(PeripheryAddresses {
position_manager: pancake_addresses.position_manager,
quoter: pancake_addresses.quoter_v2, swap_router: pancake_addresses.swap_router,
})
}
#[cfg(feature = "ramses")]
pub fn get_shadow_periphery_addresses(chain_id: u64) -> Result<PeripheryAddresses> {
let ramses_addresses =
waterpump_evm_shadow_client::addresses::get_periphery_addresses(chain_id)?;
Ok(PeripheryAddresses {
position_manager: ramses_addresses.position_manager,
quoter: ramses_addresses.quoter_v2, swap_router: ramses_addresses.swap_router,
})
}
#[cfg(feature = "slipstream")]
pub fn get_slipstream_periphery_addresses(chain_id: u64) -> Result<PeripheryAddresses> {
use waterpump_evm_slipstream_client::addresses;
Ok(PeripheryAddresses {
position_manager: addresses::get_position_manager_address(chain_id)
.map_err(|e| anyhow!("Slipstream position manager error: {}", e))?,
quoter: addresses::get_quoter_v2_address(chain_id)
.map_err(|e| anyhow!("Slipstream quoter error: {}", e))?,
swap_router: addresses::get_swap_router_address(chain_id)
.map_err(|e| anyhow!("Slipstream swap router error: {}", e))?,
})
}
struct ProjectXChainAddresses {
chain_id: u64,
position_manager: &'static str,
quoter: &'static str,
swap_router: &'static str,
}
const PROJECTX_CHAIN_ADDRESSES: &[ProjectXChainAddresses] = &[ProjectXChainAddresses {
chain_id: 999,
position_manager: "0xeaD19AE861c29bBb2101E834922B2FEee69B9091",
quoter: "0x239F11a7A3E08f2B8110D4CA9F6B95d4c8865258",
swap_router: "0x1EbDFC75FfE3ba3de61E7138a3E8706aC841Af9B",
}];
pub fn get_projectx_periphery_addresses(chain_id: u64) -> Result<PeripheryAddresses> {
let addresses = PROJECTX_CHAIN_ADDRESSES
.iter()
.find(|addr| addr.chain_id == chain_id)
.ok_or_else(|| {
anyhow!(
"ProjectX addresses not found for chain_id: {}. Supported chains: {:?}",
chain_id,
PROJECTX_CHAIN_ADDRESSES.iter().map(|a| a.chain_id).collect::<Vec<_>>()
)
})?;
Ok(PeripheryAddresses {
position_manager: Address::from_str(addresses.position_manager).with_context(|| {
format!(
"Invalid ProjectX position manager address for chain {}: {}",
chain_id, addresses.position_manager
)
})?,
quoter: Address::from_str(addresses.quoter).with_context(|| {
format!("Invalid ProjectX quoter address for chain {}: {}", chain_id, addresses.quoter)
})?,
swap_router: Address::from_str(addresses.swap_router).with_context(|| {
format!(
"Invalid ProjectX swap router address for chain {}: {}",
chain_id, addresses.swap_router
)
})?,
})
}
#[cfg(feature = "algebra")]
pub fn get_quickswap_periphery_addresses(chain_id: u64) -> Result<PeripheryAddresses> {
let addresses = waterpump_evm_algebra_client::addresses::get_deployment_addresses(chain_id)
.with_context(|| format!("Quickswap addresses not found for chain_id: {}", chain_id))?;
Ok(PeripheryAddresses {
position_manager: addresses.position_manager,
quoter: addresses.quoter,
swap_router: addresses.swap_router,
})
}
pub fn get_v4_periphery_addresses(chain_id: u64) -> Result<V4PeripheryAddresses> {
if chain_id == 143 {
return Ok(V4PeripheryAddresses {
position_manager: Address::from_str("0x5b7ec4a94ff9bedb700fb82ab09d5846972f4016")
.with_context(|| "Invalid Monad V4 position manager address")?,
pool_manager: Address::from_str("0x188d586ddcf52439676ca21a244753fa19f9ea8e")
.with_context(|| "Invalid Monad V4 pool manager address")?,
state_view: Address::from_str("0x77395f3b2e73ae90843717371294fa97cc419d64")
.with_context(|| "Invalid Monad V4 state view address")?,
});
}
let chain_addresses = CHAIN_TO_ADDRESSES_MAP
.get(&chain_id)
.ok_or_else(|| anyhow!("Chain addresses not found for chain_id: {}", chain_id))?;
let position_manager = chain_addresses
.v4_position_manager
.ok_or_else(|| anyhow!("V4 position manager not found for chain_id: {}", chain_id))?;
let pool_manager = chain_addresses
.v4_pool_manager
.ok_or_else(|| anyhow!("V4 pool manager not found for chain_id: {}", chain_id))?;
let state_view = chain_addresses
.v4_state_view
.ok_or_else(|| anyhow!("V4 state view not found for chain_id: {}", chain_id))?;
Ok(V4PeripheryAddresses { position_manager, pool_manager, state_view })
}
pub fn get_periphery_addresses(protocol: &str, chain_id: u64) -> Result<PeripheryAddresses> {
match protocol {
"v3" => get_v3_periphery_addresses(chain_id),
#[cfg(feature = "pancakeswap")]
"pancake-v3" => get_pancake_v3_periphery_addresses(chain_id),
#[cfg(feature = "ramses")]
"shadow" => get_shadow_periphery_addresses(chain_id),
#[cfg(feature = "slipstream")]
"slipstream" => get_slipstream_periphery_addresses(chain_id),
#[cfg(feature = "algebra")]
"algebra" => get_quickswap_periphery_addresses(chain_id),
"projectx" => get_projectx_periphery_addresses(chain_id),
_ => Err(anyhow!(
"Unknown protocol '{}'. Supported protocols: 'v3', 'pancake-v3', 'ramses-v3', \
'slipstream', 'algebra', 'projectx'",
protocol
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_v3_periphery_addresses_ethereum() {
let addresses = get_v3_periphery_addresses(1).unwrap();
assert!(!addresses.position_manager.is_zero());
assert!(!addresses.quoter.is_zero());
assert!(!addresses.swap_router.is_zero());
}
#[test]
fn test_get_v3_periphery_addresses_unknown_chain() {
let result = get_v3_periphery_addresses(99999);
assert!(result.is_err());
}
#[cfg(feature = "pancakeswap")]
#[test]
fn test_get_pancakeswap_v3_periphery_addresses_bsc() {
let addresses = get_pancake_v3_periphery_addresses(56).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x46A15B0b27311cedF172AB29E4f4766fbE7F4364").unwrap()
);
}
#[cfg(feature = "pancakeswap")]
#[test]
fn test_get_pancakeswap_v3_periphery_addresses_base() {
let addresses = get_pancake_v3_periphery_addresses(8453).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x46A15B0b27311cedF172AB29E4f4766fbE7F4364").unwrap()
);
}
#[cfg(feature = "pancakeswap")]
#[test]
fn test_get_pancakeswap_v3_periphery_addresses_unknown_chain() {
let result = get_pancake_v3_periphery_addresses(99999);
assert!(result.is_err());
}
#[cfg(feature = "slipstream")]
#[test]
fn test_get_slipstream_periphery_addresses_base() {
let addresses = get_slipstream_periphery_addresses(8453).unwrap();
assert!(!addresses.position_manager.is_zero());
assert!(!addresses.quoter.is_zero());
assert!(!addresses.swap_router.is_zero());
}
#[cfg(feature = "slipstream")]
#[test]
fn test_get_slipstream_periphery_addresses_unknown_chain() {
let result = get_slipstream_periphery_addresses(99999);
assert!(result.is_err());
}
#[test]
fn test_get_periphery_addresses_v3() {
let addresses = get_periphery_addresses("v3", 1).unwrap();
assert!(!addresses.position_manager.is_zero());
}
#[cfg(feature = "pancakeswap")]
#[test]
fn test_get_periphery_addresses_pancake_v3() {
let addresses = get_periphery_addresses("pancake-v3", 56).unwrap();
assert!(!addresses.position_manager.is_zero());
}
#[cfg(feature = "slipstream")]
#[test]
fn test_get_periphery_addresses_slipstream() {
let addresses = get_periphery_addresses("slipstream", 8453).unwrap();
assert!(!addresses.position_manager.is_zero());
}
#[cfg(feature = "algebra")]
#[test]
fn test_get_algebra_periphery_addresses_polygon() {
let addresses = get_quickswap_periphery_addresses(137).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x8eF88E4c7CfbbaC1C163f7eddd4B578792201de6").unwrap()
);
assert_eq!(
addresses.quoter,
Address::from_str("0xa15F0D7377B2A0C0c10db057f641beD21028FC89").unwrap()
);
assert_eq!(
addresses.swap_router,
Address::from_str("0xf5b509bB0909a69B1c207E495f687a596C168E12").unwrap()
);
}
#[cfg(feature = "algebra")]
#[test]
fn test_get_periphery_addresses_algebra() {
let addresses = get_periphery_addresses("algebra", 137).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x8eF88E4c7CfbbaC1C163f7eddd4B578792201de6").unwrap()
);
}
#[test]
fn test_get_periphery_addresses_unknown_protocol() {
let result = get_periphery_addresses("unknown", 1);
assert!(result.is_err());
}
#[test]
fn test_get_v4_periphery_addresses_monad() {
let addresses = get_v4_periphery_addresses(143).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x5b7ec4a94ff9bedb700fb82ab09d5846972f4016").unwrap()
);
assert_eq!(
addresses.pool_manager,
Address::from_str("0x188d586ddcf52439676ca21a244753fa19f9ea8e").unwrap()
);
assert_eq!(
addresses.state_view,
Address::from_str("0x77395f3b2e73ae90843717371294fa97cc419d64").unwrap()
);
}
#[test]
fn test_get_v4_periphery_addresses_unknown_chain() {
let result = get_v4_periphery_addresses(99999);
assert!(result.is_err());
}
#[cfg(feature = "ramses")]
#[test]
fn test_get_shadow_periphery_addresses_sonic() {
let addresses = get_shadow_periphery_addresses(146).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x12E66C8F215DdD5d48d150c8f46aD0c6fB0F4406").unwrap()
);
assert_eq!(
addresses.quoter,
Address::from_str("0x219b7ADebc0935a3eC889a148c6924D51A07535A").unwrap()
);
assert_eq!(
addresses.swap_router,
Address::from_str("0x5543c6176feb9b4b179078205d7c29eea2e2d695").unwrap()
);
}
#[cfg(feature = "ramses")]
#[test]
fn test_get_shadow_periphery_addresses_unknown_chain() {
let result = get_shadow_periphery_addresses(1);
assert!(result.is_err());
}
#[cfg(feature = "ramses")]
#[test]
fn test_get_periphery_addresses_shadow() {
let addresses = get_periphery_addresses("shadow", 146).unwrap();
assert_eq!(
addresses.position_manager,
Address::from_str("0x12E66C8F215DdD5d48d150c8f46aD0c6fB0F4406").unwrap()
);
}
}