tycho_simulation/price_level_stream/
fallback_router.rs1use alloy::{
8 network::Ethereum,
9 primitives::{address, Address, TxKind},
10 providers::{Provider, ProviderBuilder, RootProvider},
11 rpc::types::TransactionRequest,
12 sol,
13 sol_types::SolCall,
14};
15use tycho_common::Bytes;
16
17pub const FALLBACK_ROUTER_ADDRESS: Address = address!("4DdF368080CD7946db5b459aD591c350158175e1");
23
24sol! {
25 function getWhitelistedVenues() external view returns (address[] memory venues);
28}
29
30#[derive(Debug, thiserror::Error)]
32pub enum FetchVenuesError {
33 #[error("invalid RPC URL {url:?}: {reason}")]
35 InvalidUrl {
36 url: String,
38 reason: String,
40 },
41 #[error("getWhitelistedVenues call to the PropAMMRouter failed: {reason}")]
43 Call {
44 reason: String,
46 },
47}
48
49pub async fn fetch_fallback_router_venues(rpc_url: &str) -> Result<Vec<Bytes>, FetchVenuesError> {
59 let url: reqwest::Url = rpc_url
60 .parse()
61 .map_err(|e| FetchVenuesError::InvalidUrl {
62 url: rpc_url.to_string(),
63 reason: format!("{e}"),
64 })?;
65 let provider: RootProvider<Ethereum> = ProviderBuilder::default().connect_http(url);
66 let response = provider
67 .call(TransactionRequest {
68 to: Some(TxKind::Call(FALLBACK_ROUTER_ADDRESS)),
69 input: getWhitelistedVenuesCall {}
70 .abi_encode()
71 .into(),
72 ..Default::default()
73 })
74 .await
75 .map_err(|e| FetchVenuesError::Call { reason: e.to_string() })?;
76 let venues = getWhitelistedVenuesCall::abi_decode_returns(&response).map_err(|e| {
77 FetchVenuesError::Call { reason: format!("failed to decode response: {e}") }
78 })?;
79 Ok(venues
80 .into_iter()
81 .map(|venue| Bytes::from(venue.as_slice().to_vec()))
82 .collect())
83}
84
85#[cfg(test)]
86mod tests {
87 use std::str::FromStr;
88
89 use super::*;
90
91 #[tokio::test]
92 #[ignore = "Requires RPC_URL to be set in environment variables or .env file"]
93 async fn test_fetch_fallback_router_venues_against_mainnet() {
94 let rpc_url = std::env::var("RPC_URL").expect("RPC_URL must be set for network tests");
95
96 let venues = fetch_fallback_router_venues(&rpc_url)
97 .await
98 .expect("whitelist read should succeed");
99
100 let fermiswap =
102 Bytes::from_str("0x5979458912f80b96d30d4220af8e2e4925a33320").expect("valid address");
103 assert!(venues.contains(&fermiswap), "expected FermiSwap in {venues:?}");
104 }
105
106 #[tokio::test]
107 async fn test_fetch_fallback_router_venues_invalid_url() {
108 let result = fetch_fallback_router_venues("not a url").await;
109 assert!(matches!(result, Err(FetchVenuesError::InvalidUrl { .. })));
110 }
111
112 #[test]
115 fn test_router_address_matches_the_executor() {
116 let executor = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
117 .join("../tycho-execution/contracts/src/executors/PropAMMFallbackExecutor.sol");
118 let source = std::fs::read_to_string(&executor)
119 .unwrap_or_else(|e| panic!("failed to read {}: {e}", executor.display()));
120
121 let address = FALLBACK_ROUTER_ADDRESS.to_string();
122 assert!(source.contains(&address), "PropAMMFallbackExecutor.sol does not use {address}");
123 }
124}