1use crate::protocols::{CurveCommonContract, CurveContract, CurveProtocol};
2use alloy::primitives::{Address, Bytes, U256, address};
3use alloy::providers::{Network, Provider};
4use alloy::sol_types::SolCall;
5use alloy_evm::EvmEnv;
6use eyre::Result;
7use kabu_defi_abi::IERC20;
8use kabu_defi_address_book::TokenAddressEth;
9use kabu_evm_db::KabuDBError;
10use kabu_evm_utils::evm_call;
11use kabu_types_market::{
12 CurveError, Pool, PoolAbiEncoder, PoolClass, PoolError, PoolId, PoolProtocol, PreswapRequirement, RequiredState, SwapDirection,
13};
14use lazy_static::lazy_static;
15use revm::DatabaseRef;
16use std::any::Any;
17use std::sync::Arc;
18use tracing::error;
19
20lazy_static! {
21 static ref U256_ONE: U256 = U256::from(1);
22}
23
24pub struct CurvePool<P, N, E = CurvePoolAbiEncoder<P, N>>
25where
26 N: Network,
27 P: Provider<N> + Send + Sync + Clone + 'static,
28 E: PoolAbiEncoder + Send + Sync + 'static,
29{
30 address: Address,
31 pool_contract: Arc<CurveContract<P, N>>,
32 balances: Vec<U256>,
33 tokens: Vec<Address>,
34 underlying_tokens: Vec<Address>,
35 lp_token: Option<Address>,
36 abi_encoder: Option<Arc<E>>,
37 is_meta: bool,
38 is_native: bool,
39}
40
41impl<P, N, E> Clone for CurvePool<P, N, E>
42where
43 N: Network,
44 P: Provider<N> + Send + Sync + Clone + 'static,
45 E: PoolAbiEncoder + Send + Sync + Clone + 'static,
46{
47 fn clone(&self) -> Self {
48 Self {
49 address: self.address,
50 pool_contract: Arc::clone(&self.pool_contract),
51 balances: self.balances.clone(),
52 tokens: self.tokens.clone(),
53 underlying_tokens: self.underlying_tokens.clone(),
54 lp_token: self.lp_token,
55 abi_encoder: self.abi_encoder.clone(),
56 is_meta: self.is_meta,
57 is_native: self.is_native,
58 }
59 }
60}
61
62impl<P, N, E> CurvePool<P, N, E>
63where
64 N: Network,
65 P: Provider<N> + Send + Sync + Clone + 'static,
66 E: PoolAbiEncoder + Send + Sync + Clone + 'static,
67{
68 pub fn is_meta(&self) -> bool {
69 self.is_meta
70 }
71 pub fn curve_contract(&self) -> Arc<CurveContract<P, N>> {
72 self.pool_contract.clone()
73 }
74
75 pub fn lp_token(&self) -> Option<Address> {
76 self.lp_token
77 }
78
79 pub fn with_encoder(self, e: E) -> Self {
80 Self { abi_encoder: Some(Arc::new(e)), ..self }
81 }
82
83 pub fn get_meta_coin_idx(&self, address: Address) -> Result<u32> {
84 match self.get_coin_idx(address) {
85 Ok(i) => Ok(i),
86 Err(_) => match self.get_underlying_coin_idx(address) {
87 Ok(i) => Ok(self.tokens.len() as u32 + i - 1),
88 Err(e) => Err(e),
89 },
90 }
91 }
92 pub fn get_coin_idx(&self, address: Address) -> Result<u32> {
93 for i in 0..self.tokens.len() {
94 if address == self.tokens[i] {
95 return Ok(i as u32);
96 }
97 }
98 Err(CurveError::TokenNotInPool { token: address }.into())
99 }
100 pub fn get_underlying_coin_idx(&self, address: Address) -> Result<u32> {
101 for i in 0..self.underlying_tokens.len() {
102 if address == self.underlying_tokens[i] {
103 return Ok(i as u32);
104 }
105 }
106 Err(CurveError::TokenNotInPool { token: address }.into())
107 }
108
109 pub async fn fetch_out_amount(&self, token_address_from: Address, token_address_to: Address, amount_in: U256) -> Result<U256> {
110 let i = self.get_coin_idx(token_address_from)?;
111 let j = self.get_coin_idx(token_address_to)?;
112
113 self.pool_contract.get_dy(i, j, amount_in).await
114 }
115
116 pub async fn fetch_pool_data(client: P, pool_contract: CurveContract<P, N>) -> Result<Self> {
117 let pool_contract = Arc::new(pool_contract);
118
119 let mut tokens = CurveCommonContract::coins(client.clone(), pool_contract.get_address()).await?;
120 let mut is_native = false;
121
122 for tkn in tokens.iter_mut() {
123 if *tkn == address!("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") {
124 *tkn = TokenAddressEth::WETH;
126 is_native = true;
127 }
128 }
129
130 let lp_token = (CurveCommonContract::<P, N>::lp_token(pool_contract.get_address()).await).ok();
131
132 let (underlying_tokens, is_meta) = match pool_contract.as_ref() {
133 CurveContract::I128_2ToMeta(_interface) => (CurveProtocol::<P, N>::get_underlying_tokens(tokens[1])?, true),
134 _ => (vec![], false),
135 };
136
137 let balances = CurveCommonContract::balances(client.clone(), pool_contract.get_address()).await?;
138
139 Ok(CurvePool {
150 address: pool_contract.get_address(),
151 abi_encoder: None,
152 pool_contract,
153 balances,
154 tokens,
155 underlying_tokens,
156 lp_token,
157 is_meta,
158 is_native,
159 })
160 }
161}
162
163impl<P, N> CurvePool<P, N, CurvePoolAbiEncoder<P, N>>
164where
165 N: Network,
166 P: Provider<N> + Send + Sync + Clone + 'static,
167{
168 pub async fn fetch_pool_data_with_default_encoder(client: P, pool_contract: CurveContract<P, N>) -> Result<Self> {
169 let pool_contract = Arc::new(pool_contract);
170
171 let mut tokens = CurveCommonContract::coins(client.clone(), pool_contract.get_address()).await?;
172 let mut is_native = false;
173
174 for tkn in tokens.iter_mut() {
175 if *tkn == address!("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") {
176 *tkn = TokenAddressEth::WETH;
178 is_native = true;
179 }
180 }
181
182 let lp_token = (CurveCommonContract::<P, N>::lp_token(pool_contract.get_address()).await).ok();
183
184 let (underlying_tokens, is_meta) = match pool_contract.as_ref() {
185 CurveContract::I128_2ToMeta(_interface) => (CurveProtocol::<P, N>::get_underlying_tokens(tokens[1])?, true),
186 _ => (vec![], false),
187 };
188
189 let balances = CurveCommonContract::balances(client.clone(), pool_contract.get_address()).await?;
190
191 let mut pool = CurvePool {
192 address: pool_contract.get_address(),
193 abi_encoder: None,
194 pool_contract,
195 balances,
196 tokens,
197 underlying_tokens,
198 lp_token,
199 is_meta,
200 is_native,
201 };
202
203 let abi_encoder = Arc::new(CurvePoolAbiEncoder::new(&pool));
204
205 pool.abi_encoder = Some(abi_encoder);
206
207 Ok(pool)
208 }
209}
210
211impl<P, N, E> Pool for CurvePool<P, N, E>
212where
213 N: Network,
214 P: Provider<N> + Send + Sync + Clone + 'static,
215 E: PoolAbiEncoder + Send + Sync + Clone + 'static,
216{
217 fn as_any<'a>(&self) -> &dyn Any {
218 self
219 }
220 fn get_class(&self) -> PoolClass {
221 PoolClass::Curve
222 }
223
224 fn get_protocol(&self) -> PoolProtocol {
225 PoolProtocol::Curve
226 }
227
228 fn get_address(&self) -> PoolId {
229 PoolId::Address(self.address)
230 }
231
232 fn get_pool_id(&self) -> PoolId {
233 PoolId::Address(self.address)
234 }
235
236 fn get_fee(&self) -> U256 {
237 U256::ZERO
238 }
239
240 fn get_tokens(&self) -> Vec<Address> {
241 self.tokens.clone()
242 }
243
244 fn get_swap_directions(&self) -> Vec<SwapDirection> {
245 let mut ret: Vec<SwapDirection> = Vec::new();
246 if self.is_meta {
247 ret.push((self.tokens[0], self.tokens[1]).into());
248 ret.push((self.tokens[1], self.tokens[0]).into());
249 for j in 0..self.underlying_tokens.len() {
250 ret.push((self.tokens[0], self.underlying_tokens[j]).into());
251 ret.push((self.underlying_tokens[j], self.tokens[0]).into());
252 }
253 } else {
254 for i in 0..self.tokens.len() {
255 for j in 0..self.tokens.len() {
256 if i == j {
257 continue;
258 }
259 ret.push((self.tokens[i], self.tokens[j]).into());
260 }
261 if let Some(lp_token_address) = self.lp_token {
262 ret.push((self.tokens[i], lp_token_address).into());
263 ret.push((lp_token_address, self.tokens[i]).into());
264 }
265 }
266 }
267 ret
268 }
269
270 fn calculate_out_amount(
271 &self,
272 db: &dyn DatabaseRef<Error = KabuDBError>,
273 evm_env: &EvmEnv,
274 token_address_from: &Address,
275 token_address_to: &Address,
276 in_amount: U256,
277 ) -> Result<(U256, u64), PoolError> {
278 let token_address_from = *token_address_from;
279 let token_address_to = *token_address_to;
280
281 let call_data = if self.is_meta {
282 let i: Result<u32> = self.get_coin_idx(token_address_from);
283 let j: Result<u32> = self.get_coin_idx(token_address_to);
284 if let (Ok(i), Ok(j)) = (i, j) {
285 self.pool_contract
286 .get_dy_call_data(i, j, in_amount)
287 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to get dy call data" })?
288 } else {
289 let i: u32 = self
290 .get_meta_coin_idx(token_address_from)
291 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_from }))?;
292 let j: u32 = self
293 .get_meta_coin_idx(token_address_to)
294 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_to }))?;
295 self.pool_contract
296 .get_dy_underlying_call_data(i, j, in_amount)
297 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to get dy underlying call data" })?
298 }
299 } else if let Some(lp_token) = self.lp_token {
300 if token_address_from == lp_token {
301 let i: u32 = self
302 .get_coin_idx(token_address_to)
303 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_to }))?;
304 self.pool_contract
305 .calc_withdraw_one_coin_call_data(i, in_amount)
306 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to calc withdraw one coin call data" })?
307 } else if token_address_to == lp_token {
308 let i: u32 = self
309 .get_coin_idx(token_address_from)
310 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_from }))?;
311 self.pool_contract
312 .calc_token_amount_call_data(i, in_amount)
313 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to calc token amount call data" })?
314 } else {
315 let i: u32 = self
316 .get_coin_idx(token_address_from)
317 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_from }))?;
318 let j: u32 = self
319 .get_coin_idx(token_address_to)
320 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_to }))?;
321 self.pool_contract
322 .get_dy_call_data(i, j, in_amount)
323 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to get dy call data" })?
324 }
325 } else {
326 let i: u32 = self
327 .get_coin_idx(token_address_from)
328 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_from }))?;
329 let j: u32 = self
330 .get_coin_idx(token_address_to)
331 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_to }))?;
332 self.pool_contract
333 .get_dy_call_data(i, j, in_amount)
334 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to get dy call data" })?
335 };
336
337 let (value, gas_used, _) = evm_call(db, evm_env.clone(), self.address, call_data.to_vec())?;
338
339 let ret = if value.len() > 32 { U256::from_be_slice(&value[0..32]) } else { U256::from_be_slice(&value[0..]) };
340
341 if ret.is_zero() {
342 Err(CurveError::OutAmountIsZero.into())
343 } else {
344 Ok((ret.checked_sub(*U256_ONE).ok_or(PoolError::InvalidInput { reason: "SUB_OVERFLOWN" })?, gas_used))
345 }
346 }
347
348 fn calculate_in_amount(
349 &self,
350 db: &dyn DatabaseRef<Error = KabuDBError>,
351 evm_env: &EvmEnv,
352 token_address_from: &Address,
353 token_address_to: &Address,
354 out_amount: U256,
355 ) -> Result<(U256, u64), PoolError> {
356 let token_address_from = *token_address_from;
357 let token_address_to = *token_address_to;
358
359 if self.pool_contract.can_calculate_in_amount() {
360 let i: u32 = self
361 .get_coin_idx(token_address_from)
362 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_from }))?;
363 let j: u32 = self
364 .get_coin_idx(token_address_to)
365 .map_err(|_e| PoolError::Curve(CurveError::TokenNotInPool { token: token_address_to }))?;
366 let call_data = self
367 .pool_contract
368 .get_dx_call_data(i, j, out_amount)
369 .map_err(|_e| PoolError::InvalidInput { reason: "Failed to get dx call data" })?;
370
371 let (value, gas_used, _) = evm_call(db, evm_env.clone(), self.address, call_data.to_vec())?;
372
373 let ret = if value.len() > 32 { U256::from_be_slice(&value[0..32]) } else { U256::from_be_slice(&value[0..]) };
374
375 if ret.is_zero() {
376 Err(CurveError::InAmountIsZero.into())
377 } else {
378 Ok((ret.checked_add(*U256_ONE).ok_or(PoolError::InvalidInput { reason: "ADD_OVERFLOWN" })?, gas_used))
379 }
380 } else {
381 Err(PoolError::NotImplemented)
382 }
383 }
384
385 fn can_flash_swap(&self) -> bool {
386 false
387 }
388
389 fn can_calculate_in_amount(&self) -> bool {
390 self.pool_contract.can_calculate_in_amount()
391 }
392
393 fn get_abi_encoder(&self) -> Option<&dyn PoolAbiEncoder> {
394 let r = self.abi_encoder.as_ref().unwrap().as_ref();
395 Some(r as &dyn PoolAbiEncoder)
396 }
397
398 fn get_read_only_cell_vec(&self) -> Vec<U256> {
399 Vec::new()
400 }
401
402 fn get_state_required(&self) -> Result<RequiredState> {
403 let mut state_reader = RequiredState::new();
404
405 if self.is_meta {
406 match &self.pool_contract.as_ref() {
407 CurveContract::I128_2ToMeta(_interface) => {
408 for j in 0..self.underlying_tokens.len() {
409 let value = self.balances[0] / U256::from(10);
410 match self.pool_contract.get_dy_call_data(0_u32, (j + self.tokens.len()) as u32, value) {
411 Ok(data) => {
412 state_reader.add_call(self.address, data);
413 }
414 Err(e) => {
415 error!("{}", e);
416 }
417 }
418 }
419 }
420 _ => {
421 error!("CURVE_META_POOL_NOT_SUPPORTED")
422 }
423 }
424 } else {
425 if let Some(_lp_token) = self.lp_token {
426 for i in 0..self.tokens.len() {
427 let value = self.balances[i] / U256::from(10);
428 match self.pool_contract.get_add_liquidity_call_data(i as u32, value, Address::ZERO) {
429 Ok(data) => {
430 state_reader.add_call(self.address, data);
431 }
432 Err(e) => {
433 error!("{}", e);
434 }
435 }
436 }
437 }
438
439 for i in 0..self.tokens.len() {
440 for j in 0..self.tokens.len() {
441 if i == j {
442 continue;
443 }
444 if let Some(balance) = self.balances.get(i) {
445 let value = balance / U256::from(100);
446 match self.pool_contract.get_dy_call_data(i as u32, j as u32, value) {
447 Ok(data) => {
448 state_reader.add_call(self.address, data);
449 }
450 Err(e) => {
451 error!("{}", e);
452 }
453 }
454 } else {
455 error!("Cannot get curve pool balance {} {}", self.address, i);
456 return Err(CurveError::InvalidPoolState.into());
457 }
458 }
459 }
460 }
461
462 if !self.tokens.is_empty() && self.tokens.len() >= 2 {
465 if let Ok(data) = self.pool_contract.get_dy_call_data(0, 1, U256::from(1)) {
467 state_reader.add_call(self.address, data);
468 }
469 }
470
471 state_reader.add_slot_range(self.address, U256::from(0), 0x20);
472
473 for token_address in self.get_tokens() {
474 state_reader.add_call(token_address, IERC20::balanceOfCall { account: self.address }.abi_encode());
475 }
476 Ok(state_reader)
477 }
478
479 fn is_native(&self) -> bool {
480 self.is_native
481 }
482
483 fn preswap_requirement(&self) -> PreswapRequirement {
484 PreswapRequirement::Allowance
485 }
486}
487
488#[allow(dead_code)]
489#[derive(Clone)]
490pub struct CurvePoolAbiEncoder<P, N>
491where
492 N: Network,
493 P: Provider<N> + Send + Sync + Clone + 'static,
494{
495 pool_address: Address,
496 tokens: Vec<Address>,
497 underlying_tokens: Option<Vec<Address>>,
498 lp_token: Option<Address>,
499 is_meta: bool,
500 is_native: bool,
501 curve_contract: Arc<CurveContract<P, N>>,
502}
503
504impl<P, N> CurvePoolAbiEncoder<P, N>
505where
506 N: Network,
507 P: Provider<N> + Send + Sync + Clone + 'static,
508{
509 pub fn new(pool: &CurvePool<P, N>) -> Self {
510 Self {
511 pool_address: pool.address,
512 tokens: pool.tokens.clone(),
513 underlying_tokens: if pool.underlying_tokens.is_empty() { None } else { Some(pool.underlying_tokens.clone()) },
514 lp_token: pool.lp_token,
515 is_meta: pool.is_meta,
516 is_native: pool.is_native,
517 curve_contract: pool.pool_contract.clone(),
518 }
519 }
520
521 pub fn get_meta_coin_idx(&self, address: Address) -> Result<u32> {
522 match self.get_coin_idx(address) {
523 Ok(idx) => Ok(idx),
524 _ => match self.get_underlying_coin_idx(address) {
525 Ok(idx) => Ok(idx + self.tokens.len() as u32 - 1),
526 Err(_) => Err(CurveError::TokenNotInPool { token: address }.into()),
527 },
528 }
529 }
530
531 pub fn get_coin_idx(&self, address: Address) -> Result<u32> {
532 for i in 0..self.tokens.len() {
533 if address == self.tokens[i] {
534 return Ok(i as u32);
535 }
536 }
537 Err(CurveError::TokenNotInPool { token: address }.into())
538 }
539
540 pub fn get_underlying_coin_idx(&self, address: Address) -> Result<u32> {
541 match &self.underlying_tokens {
542 Some(underlying_tokens) => {
543 for (i, token_address) in underlying_tokens.iter().enumerate() {
544 if address == *token_address {
545 return Ok(i as u32);
546 }
547 }
548 Err(CurveError::InvalidPoolState.into())
549 }
550 _ => Err(CurveError::InvalidPoolState.into()),
551 }
552 }
553}
554
555impl<P, N> PoolAbiEncoder for CurvePoolAbiEncoder<P, N>
556where
557 N: Network,
558 P: Provider<N> + Send + Sync + Clone + 'static,
559{
560 fn encode_swap_in_amount_provided(
561 &self,
562 token_from_address: Address,
563 token_to_address: Address,
564 amount: U256,
565 recipient: Address,
566 _payload: Bytes,
567 ) -> Result<Bytes> {
568 if self.is_meta {
569 let i: Result<u32> = self.get_coin_idx(token_from_address);
570 let j: Result<u32> = self.get_coin_idx(token_to_address);
571
572 match (i, j) {
573 (Ok(i), Ok(j)) => self.curve_contract.get_exchange_call_data(i, j, amount, U256::ZERO, recipient),
574 _ => {
575 let meta_i: u32 = self.get_meta_coin_idx(token_from_address)?;
576 let meta_j: u32 = self.get_meta_coin_idx(token_to_address)?;
577 self.curve_contract.get_exchange_underlying_call_data(meta_i, meta_j, amount, U256::ZERO, recipient)
578 }
579 }
580 } else if let Some(lp_token) = self.lp_token {
581 if token_from_address == lp_token {
582 let i: u32 = self.get_coin_idx(token_to_address)?;
583 self.curve_contract.get_remove_liquidity_one_coin_call_data(i, amount, recipient)
584 } else if token_to_address == lp_token {
585 let i: u32 = self.get_coin_idx(token_from_address)?;
586 self.curve_contract.get_add_liquidity_call_data(i, amount, recipient)
587 } else {
588 let i: u32 = self.get_coin_idx(token_from_address)?;
589 let j: u32 = self.get_coin_idx(token_to_address)?;
590 self.curve_contract.get_exchange_call_data(i, j, amount, U256::ZERO, recipient)
591 }
592 } else {
593 let i: u32 = self.get_coin_idx(token_from_address)?;
594 let j: u32 = self.get_coin_idx(token_to_address)?;
595 self.curve_contract.get_exchange_call_data(i, j, amount, U256::ZERO, recipient)
596 }
597 }
598
599 fn encode_swap_out_amount_provided(
600 &self,
601 _token_from_address: Address,
602 _token_to_address: Address,
603 _amount: U256,
604 _recipient: Address,
605 _payload: Bytes,
606 ) -> Result<Bytes> {
607 Err(PoolError::NotImplemented.into())
608 }
609
610 fn swap_in_amount_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
611 Some(0x44)
612 }
613
614 fn swap_out_amount_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
615 None
616 }
617 fn swap_out_amount_return_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
618 None
619 }
620 fn swap_in_amount_return_offset(&self, _token_from_address: Address, _token_to_address: Address) -> Option<u32> {
621 None
622 }
623 fn swap_out_amount_return_script(&self, _token_from_address: Address, _token_to_address: Address) -> Option<Bytes> {
624 None
625 }
626
627 fn swap_in_amount_return_script(&self, _token_from_address: Address, _token_to_address: Address) -> Option<Bytes> {
628 None
629 }
630}
631
632#[cfg(test)]
633mod tests {
634 use eyre::Result;
635
636 use crate::CurvePool;
637 use crate::protocols::CurveProtocol;
638 use alloy::primitives::U256;
639 use alloy::providers::Provider;
640 use alloy::rpc::types::BlockNumberOrTag;
641 use alloy_evm::EvmEnv;
642 use env_logger::Env as EnvLog;
643 use kabu_evm_db::{DatabaseKabuExt, KabuDBType};
644 use kabu_node_debug_provider::AnvilDebugProviderFactory;
645 use kabu_types_market::MarketState;
646 use kabu_types_market::Pool;
647 use kabu_types_market::RequiredStateReader;
648 use revm::database::CacheDB;
649 use tracing::{debug, error};
650
651 #[ignore]
652 #[tokio::test]
653 async fn test_pool() -> Result<()> {
654 let _ = env_logger::try_init_from_env(EnvLog::default().default_filter_or("info,alloy_rpc_client=off"));
655
656 dotenvy::from_filename(".env.test").ok();
657 let node_url = std::env::var("MAINNET_WS")?;
658
659 let client = AnvilDebugProviderFactory::from_node_on_block(node_url, 20045799).await?;
660
661 let mut market_state = MarketState::new(KabuDBType::new());
662
663 let curve_contracts = CurveProtocol::get_contracts_vec(client.clone());
664
665 for curve_contract in curve_contracts.into_iter() {
666 debug!("Loading Pool : {} {:?}", curve_contract.get_address(), curve_contract);
667 let pool = CurvePool::fetch_pool_data_with_default_encoder(client.clone(), curve_contract).await.unwrap();
668 let state_required = pool.get_state_required().unwrap();
669
670 let state_required = RequiredStateReader::fetch_calls_and_slots(client.clone(), state_required, Some(20045799)).await.unwrap();
671 debug!("Pool state fetched {} {}", pool.address, state_required.len());
672
673 market_state.state_db.apply_geth_update(state_required);
674 debug!(
675 "Pool : {} Accs : {} Storage : {}",
676 pool.address,
677 market_state.state_db.accounts_len(),
678 market_state.state_db.storage_len()
679 );
680
681 let block_header = client.get_block_by_number(BlockNumberOrTag::Number(20045799)).await.unwrap().unwrap().header;
682 debug!("Block {} {}", block_header.number, block_header.timestamp);
683
684 let cache_db = CacheDB::new(market_state.state_db.clone());
685
686 let tokens = pool.tokens.clone();
687 let balances = pool.balances.clone();
688 for i in 0..tokens.len() {
689 for j in 0..tokens.len() {
690 if i == j {
691 continue;
692 }
693 let in_amount = balances[i] / U256::from(100);
694 let token_in = tokens[i];
695 let token_out = tokens[j];
696 let (out_amount, gas_used) =
697 match pool.calculate_out_amount(&cache_db, &EvmEnv::default(), &token_in, &token_out, in_amount) {
698 Ok(result) => result,
699 Err(e) => {
700 error!("Failed to calculate out amount for pool {:?}: {}", pool.get_address(), e);
701 (U256::ZERO, 0)
702 }
703 };
704 debug!(
705 "Calculated : {:?} {} -> {} : {} -> {} gas : {}",
706 pool.get_address(),
707 tokens[i],
708 tokens[j],
709 in_amount,
710 out_amount,
711 gas_used
712 );
713
714 let out_amount_fetched = pool.fetch_out_amount(token_in, token_out, in_amount).await.unwrap();
715 debug!("Fetched {:?} {} -> {} : {} -> {}", pool.get_address(), tokens[i], tokens[j], in_amount, out_amount_fetched);
716 assert_eq!(out_amount, out_amount_fetched - U256::from(1));
717 }
718 }
719
720 if let Some(lp_token) = pool.lp_token {
721 for i in 0..tokens.len() {
722 let in_amount = balances[i] / U256::from(1000);
723 let token_in = tokens[i];
724 let (out_amount, gas_used) =
725 pool.calculate_out_amount(&cache_db, &EvmEnv::default(), &token_in, &lp_token, in_amount).unwrap_or_default();
726 debug!("LP {:?} {} -> {} : {} -> {} gas : {}", pool.get_address(), token_in, lp_token, in_amount, out_amount, gas_used);
727 assert!(gas_used > 50000);
728
729 let (out_amount2, gas_used) =
730 pool.calculate_out_amount(&cache_db, &EvmEnv::default(), &lp_token, &token_in, out_amount).unwrap_or_default();
731 debug!(
732 "LP {:?} {} -> {} : {} -> {} gas : {}",
733 pool.get_address(),
734 lp_token,
735 token_in,
736 out_amount,
737 out_amount2,
738 gas_used
739 );
740 assert!(gas_used > 50000);
741 assert_ne!(out_amount, U256::ZERO);
742 assert_ne!(out_amount2, U256::ZERO);
743 }
744 }
745
746 if pool.is_meta {
747 let underlying_tokens = pool.underlying_tokens.clone();
748 for underlying_token in underlying_tokens {
749 let in_amount = balances[0] / U256::from(1000);
750 let token_in = tokens[0];
751 let token_out = underlying_token;
752 let (out_amount, gas_used) =
753 pool.calculate_out_amount(&cache_db, &EvmEnv::default(), &token_in, &token_out, in_amount).unwrap_or_default();
754 debug!(
755 "Meta {:?} {} -> {} : {} -> {} gas: {}",
756 pool.get_address(),
757 token_in,
758 token_out,
759 in_amount,
760 out_amount,
761 gas_used
762 );
763 let (out_amount2, gas_used) =
764 pool.calculate_out_amount(&cache_db, &EvmEnv::default(), &token_out, &token_in, out_amount).unwrap_or_default();
765 debug!(
766 "Meta {:?} {} -> {} : {} -> {} gas : {} ",
767 pool.get_address(),
768 token_out,
769 token_in,
770 out_amount,
771 out_amount2,
772 gas_used
773 );
774 assert_ne!(out_amount, U256::ZERO);
775 assert_ne!(out_amount2, U256::ZERO);
776 }
777 }
778 }
779 Ok(())
780 }
781}