1use std::{collections::HashMap, str::FromStr};
2
3use tycho_common::{models::Chain, Bytes};
4
5use crate::encoding::{
6 errors::EncodingError,
7 evm::{
8 constants::{
9 DEFAULT_EXECUTORS_JSON, PRICE_LEVEL_STREAM_KEY, PRICE_LEVEL_STREAM_PREFIX,
10 PROPAMM_FALLBACK_KEY, PROPAMM_FALLBACK_PREFIX, PROTOCOL_SPECIFIC_CONFIG,
11 },
12 swap_encoder::{
13 aerodrome_v1::AerodromeV1SwapEncoder, balancer_v2::BalancerV2SwapEncoder,
14 balancer_v3::BalancerV3SwapEncoder, bebop::BebopSwapEncoder, bopamm::BopAMMSwapEncoder,
15 curve::CurveSwapEncoder, ekubo::EkuboSwapEncoder, ekubo_v3::EkuboV3SwapEncoder,
16 erc_4626::ERC4626SwapEncoder, etherfi::EtherfiSwapEncoder, fermiswap::FermiSwapEncoder,
17 fluid_v1::FluidV1SwapEncoder, hashflow::HashflowSwapEncoder,
18 liquidity_party::LiquidityPartySwapEncoder, liquorice::LiquoriceSwapEncoder,
19 lunarbase::LunarBaseSwapEncoder, maverick_v2::MaverickV2SwapEncoder,
20 metric::MetricSwapEncoder, native::NativeSwapEncoder, native_wrap::WrapSwapEncoder,
21 propamm::PropAMMSwapEncoder, ring_swap_v2::RingSwapV2SwapEncoder,
22 rocketpool::RocketpoolSwapEncoder, sky::SkySwapEncoder,
23 slipstreams::SlipstreamsSwapEncoder, uniswap_v2::UniswapV2SwapEncoder,
24 uniswap_v3::UniswapV3SwapEncoder, uniswap_v4::UniswapV4SwapEncoder,
25 },
26 },
27 swap_encoder::SwapEncoder,
28};
29
30#[derive(Clone)]
32pub struct SwapEncoderRegistry {
33 chain: Chain,
34 encoders: HashMap<String, Box<dyn SwapEncoder>>,
36}
37
38impl SwapEncoderRegistry {
39 pub fn new(chain: Chain) -> Self {
40 Self { chain, encoders: HashMap::new() }
41 }
42
43 pub fn new_with_defaults(chain: Chain) -> Result<Self, EncodingError> {
45 Self::new(chain).add_default_encoders(None)
46 }
47
48 pub fn add_default_encoders(
51 mut self,
52 executors_addresses: Option<String>,
53 ) -> Result<Self, EncodingError> {
54 let config_str = if let Some(addresses) = executors_addresses {
55 addresses
56 } else {
57 DEFAULT_EXECUTORS_JSON.to_string()
58 };
59 let config: HashMap<Chain, HashMap<String, String>> = serde_json::from_str(&config_str)?;
60 let executors = config
61 .get(&self.chain)
62 .ok_or(EncodingError::FatalError("No executors found for chain".to_string()))?;
63
64 let protocol_specific_config: HashMap<Chain, HashMap<String, HashMap<String, String>>> =
65 serde_json::from_str(PROTOCOL_SPECIFIC_CONFIG)?;
66 let protocol_specific_config = protocol_specific_config
67 .get(&self.chain)
68 .ok_or(EncodingError::FatalError(
69 "No protocol specific config found for chain".to_string(),
70 ))?;
71 for (protocol, executor_address) in executors {
72 let encoder = self.create_encoder(
73 protocol,
74 Bytes::from_str(executor_address).map_err(|_| {
75 EncodingError::FatalError(format!(
76 "Invalid executor address for protocol {}",
77 protocol
78 ))
79 })?,
80 protocol_specific_config
81 .get(protocol)
82 .cloned(),
83 )?;
84 self.encoders
85 .insert(protocol.to_string(), encoder);
86 }
87 Ok(self)
88 }
89
90 pub fn register_encoder(mut self, protocol: &str, encoder: Box<dyn SwapEncoder>) -> Self {
92 self.encoders
93 .insert(protocol.to_string(), encoder);
94 self
95 }
96
97 #[allow(clippy::borrowed_box)]
104 pub fn get_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
105 if let Some(encoder) = self.encoders.get(protocol_system) {
106 return Some(encoder);
107 }
108 if protocol_system.starts_with(PRICE_LEVEL_STREAM_PREFIX) {
109 return self
110 .encoders
111 .get(PRICE_LEVEL_STREAM_KEY);
112 }
113 if protocol_system.starts_with(PROPAMM_FALLBACK_PREFIX) {
114 return self.encoders.get(PROPAMM_FALLBACK_KEY);
115 }
116 None
117 }
118
119 pub fn executor_addresses(&self) -> HashMap<String, Bytes> {
124 self.encoders
125 .iter()
126 .map(|(protocol, encoder)| (protocol.clone(), encoder.executor_address().clone()))
127 .collect()
128 }
129
130 fn create_encoder(
131 &self,
132 protocol_system: &str,
133 executor_address: Bytes,
134 config: Option<HashMap<String, String>>,
135 ) -> Result<Box<dyn SwapEncoder>, EncodingError> {
136 match protocol_system {
137 "uniswap_v2" | "sushiswap_v2" | "pancakeswap_v2" | "quickswap_v2" => {
138 Ok(Box::new(UniswapV2SwapEncoder::new(executor_address, self.chain, config)?))
139 }
140 "ring_swap_v2" => {
141 Ok(Box::new(RingSwapV2SwapEncoder::new(executor_address, self.chain, config)?))
142 }
143 "aerodrome_v1" => {
144 Ok(Box::new(AerodromeV1SwapEncoder::new(executor_address, self.chain, config)?))
145 }
146 "vm:balancer_v2" => {
147 Ok(Box::new(BalancerV2SwapEncoder::new(executor_address, self.chain, config)?))
148 }
149 "uniswap_v3" | "pancakeswap_v3" | "sushiswap_v3" | "robinswap_v3" => {
150 Ok(Box::new(UniswapV3SwapEncoder::new(executor_address, self.chain, config)?))
151 }
152 "uniswap_v4" => {
153 Ok(Box::new(UniswapV4SwapEncoder::new(executor_address, self.chain, config)?))
154 }
155 "ekubo_v2" => {
156 Ok(Box::new(EkuboSwapEncoder::new(executor_address, self.chain, config)?))
157 }
158 "ekubo_v3" => {
159 Ok(Box::new(EkuboV3SwapEncoder::new(executor_address, self.chain, config)?))
160 }
161 "vm:bopamm" => {
162 Ok(Box::new(BopAMMSwapEncoder::new(executor_address, self.chain, config)?))
163 }
164 "vm:curve" => {
165 Ok(Box::new(CurveSwapEncoder::new(executor_address, self.chain, config)?))
166 }
167 "vm:maverick_v2" => {
168 Ok(Box::new(MaverickV2SwapEncoder::new(executor_address, self.chain, config)?))
169 }
170 "vm:balancer_v3" => {
171 Ok(Box::new(BalancerV3SwapEncoder::new(executor_address, self.chain, config)?))
172 }
173 "rfq:bebop" => {
174 Ok(Box::new(BebopSwapEncoder::new(executor_address, self.chain, config)?))
175 }
176 "rfq:hashflow" => {
177 Ok(Box::new(HashflowSwapEncoder::new(executor_address, self.chain, config)?))
178 }
179 "rfq:liquorice" => {
180 Ok(Box::new(LiquoriceSwapEncoder::new(executor_address, self.chain, config)?))
181 }
182 "rfq:metric" => {
183 Ok(Box::new(MetricSwapEncoder::new(executor_address, self.chain, config)?))
184 }
185 "rfq:native" => {
186 Ok(Box::new(NativeSwapEncoder::new(executor_address, self.chain, config)?))
187 }
188 "fluid_v1" => {
189 Ok(Box::new(FluidV1SwapEncoder::new(executor_address, self.chain, config)?))
190 }
191 "vm:fermiswap" => {
192 Ok(Box::new(FermiSwapEncoder::new(executor_address, self.chain, config)?))
193 }
194 "vm:liquidityparty" => {
195 Ok(Box::new(LiquidityPartySwapEncoder::new(executor_address, self.chain, config)?))
196 }
197 "aerodrome_slipstreams" => {
198 Ok(Box::new(SlipstreamsSwapEncoder::new(executor_address, self.chain, config)?))
199 }
200 "rocketpool" => {
201 Ok(Box::new(RocketpoolSwapEncoder::new(executor_address, self.chain, config)?))
202 }
203 "sky" => Ok(Box::new(SkySwapEncoder::new(executor_address, self.chain, config)?)),
204 "erc4626" => {
205 Ok(Box::new(ERC4626SwapEncoder::new(executor_address, self.chain, config)?))
206 }
207 "lunarbase" => {
208 Ok(Box::new(LunarBaseSwapEncoder::new(executor_address, self.chain, config)?))
209 }
210 "velodrome_slipstreams" => {
211 Ok(Box::new(SlipstreamsSwapEncoder::new(executor_address, self.chain, config)?))
212 }
213 "up_v3" => {
216 Ok(Box::new(SlipstreamsSwapEncoder::new(executor_address, self.chain, config)?))
217 }
218 "ramses_v3" => {
233 Ok(Box::new(SlipstreamsSwapEncoder::new(executor_address, self.chain, config)?))
234 }
235 "native_wrapper" => {
236 Ok(Box::new(WrapSwapEncoder::new(executor_address, self.chain, config)?))
237 }
238 "etherfi" => {
239 Ok(Box::new(EtherfiSwapEncoder::new(executor_address, self.chain, config)?))
240 }
241 pls if pls == PRICE_LEVEL_STREAM_KEY ||
248 pls.starts_with(PRICE_LEVEL_STREAM_PREFIX) ||
249 pls == PROPAMM_FALLBACK_KEY ||
250 pls.starts_with(PROPAMM_FALLBACK_PREFIX) =>
251 {
252 Ok(Box::new(PropAMMSwapEncoder::new(executor_address, self.chain, config)?))
253 }
254 _ => Err(EncodingError::FatalError(format!(
255 "Unknown protocol system: {}",
256 protocol_system
257 ))),
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
271 fn test_price_level_stream_protocols_route_to_generic_encoder() {
272 let executors = std::fs::read_to_string("config/test_executor_addresses.json").unwrap();
273 let registry = SwapEncoderRegistry::new(Chain::Ethereum)
274 .add_default_encoders(Some(executors))
275 .unwrap();
276
277 for protocol in [
278 PRICE_LEVEL_STREAM_KEY,
279 "pricelevelstream:fermiswap",
280 "pricelevelstream:kipseli",
281 "pricelevelstream:0x2222222222222222222222222222222222222222",
282 ] {
283 assert!(registry.get_encoder(protocol).is_some(), "no encoder resolved for {protocol}");
284 }
285 assert!(registry
287 .get_encoder("unknown_protocol")
288 .is_none());
289 }
290
291 #[test]
294 fn test_propamm_fallback_protocol_resolution() {
295 let executors = std::fs::read_to_string("config/test_executor_addresses.json").unwrap();
296 let registry = SwapEncoderRegistry::new(Chain::Ethereum)
297 .add_default_encoders(Some(executors))
298 .unwrap();
299
300 for protocol in [
301 PROPAMM_FALLBACK_KEY,
302 "propammfallback:fermiswap",
303 "propammfallback:0x5979458912f80b96d30d4220af8e2e4925a33320",
304 ] {
305 assert!(registry.get_encoder(protocol).is_some(), "no encoder resolved for {protocol}");
306 }
307
308 let direct = registry
309 .get_encoder("pricelevelstream:fermiswap")
310 .unwrap()
311 .executor_address()
312 .clone();
313 let via_router = registry
314 .get_encoder("propammfallback:fermiswap")
315 .unwrap()
316 .executor_address()
317 .clone();
318 assert_ne!(direct, via_router);
319 }
320
321 #[test]
322 fn test_default_encoders_build_for_every_configured_chain() {
323 let chains = [
324 Chain::Ethereum,
325 Chain::Base,
326 Chain::Unichain,
327 Chain::Arbitrum,
328 Chain::Bsc,
329 Chain::Polygon,
330 Chain::Plasma,
331 Chain::Robinhood,
332 ];
333 for chain in chains {
334 let registry = SwapEncoderRegistry::new_with_defaults(chain).unwrap_or_else(|e| {
335 panic!("default encoders failed to build for chain {chain}: {e}")
336 });
337 assert!(
338 registry
339 .get_encoder("uniswap_v3")
340 .is_some(),
341 "chain {chain} is missing the uniswap_v3 encoder"
342 );
343 }
344 }
345
346 #[test]
347 fn test_executor_addresses_match_registered_encoders() {
348 let registry = SwapEncoderRegistry::new_with_defaults(Chain::Ethereum).unwrap();
349
350 let executor_addresses = registry.executor_addresses();
351
352 assert!(!executor_addresses.is_empty());
353 for (protocol, executor_address) in executor_addresses {
354 let encoder = registry
355 .get_encoder(&protocol)
356 .unwrap_or_else(|| panic!("no encoder registered for {protocol}"));
357 assert_eq!(encoder.executor_address(), &executor_address);
358 }
359 }
360}