From d9006d888c67f0c9108f865fa1442e794de4fd13 Mon Sep 17 00:00:00 2001 From: adel Date: Mon, 30 Sep 2024 10:57:47 +0200 Subject: [PATCH] fix(smol_stuff): Smol stuff (#58) --- .gitignore | 2 + config.yaml | 12 ++--- src/services/oracle.rs | 4 +- src/types/position.rs | 107 ++++++----------------------------------- src/utils/ekubo.rs | 83 ++++++++++++++++++++++++++++++++ src/utils/mod.rs | 1 + 6 files changed, 109 insertions(+), 100 deletions(-) create mode 100644 src/utils/ekubo.rs diff --git a/.gitignore b/.gitignore index 7825571..fc90cde 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ data.json indexer.log run_indexer.sh src/bindings + +*.json \ No newline at end of file diff --git a/config.yaml b/config.yaml index c788305..3978de0 100644 --- a/config.yaml +++ b/config.yaml @@ -14,34 +14,34 @@ assets: ticker: "ETH" decimals: 18 mainnet_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" - sepolia_address: "" + sepolia_address: "0x7809bb63f557736e49ff0ae4a64bd8aa6ea60e3f77f26c520cb92c24e3700d3" - name: "wrapped-bitcoin" ticker: "WBTC" decimals: 8 mainnet_address: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac" - sepolia_address: "" + sepolia_address: "0x063d32a3fa6074e72e7a1e06fe78c46a0c8473217773e19f11d8c8cbfc4ff8ca" - name: "usd-coin" ticker: "USDC" decimals: 6 mainnet_address: "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8" - sepolia_address: "" + sepolia_address: "0x027ef4670397069d7d5442cb7945b27338692de0d8896bdb15e6400cf5249f94" - name: "tether" ticker: "USDT" decimals: 6 mainnet_address: "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8" - sepolia_address: "" + sepolia_address: "0x002cd937c3dccd4a4e125011bbe3189a6db0419bb6dd95c4b5ce5f6d834d8996" - name: "wrapped-steth" ticker: "WSTETH" decimals: 18 mainnet_address: "0x42b8f0484674ca266ac5d08e4ac6a3fe65bd3129795def2dca5c34ecc5f96d2" - sepolia_address: "" + sepolia_address: "0x057181b39020af1416747a7d0d2de6ad5a5b721183136585e8774e1425efd5d2" - name: "starknet" ticker: "STRK" decimals: 18 mainnet_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" - sepolia_address: "" + sepolia_address: "0x0772131070c7d56f78f3e46b27b70271d8ca81c7c52e3f62aa868fab4b679e43" diff --git a/src/services/oracle.rs b/src/services/oracle.rs index 8e27935..fdefcbe 100644 --- a/src/services/oracle.rs +++ b/src/services/oracle.rs @@ -14,7 +14,7 @@ use crate::cli::NetworkName; use crate::{config::Config, utils::conversions::hex_str_to_big_decimal}; const USD_ASSET: &str = "usd"; -const PRICES_UPDATE_INTERVAL: u64 = 60; // update every minutes +const PRICES_UPDATE_INTERVAL: u64 = 30; // update every 30 seconds pub struct OracleService { oracle: PragmaOracle, @@ -147,7 +147,7 @@ impl PragmaOracle { fn fetch_price_url(&self, base: String, quote: String) -> String { format!( "{}node/v1/onchain/{}/{}?network={}&components=false&variations=false&interval={}&aggregation={}", - self.api_url, base, quote,self.network, self.interval, self.aggregation_method + self.api_url, base, quote, self.network, self.interval, self.aggregation_method ) } } diff --git a/src/types/position.rs b/src/types/position.rs index 2696db0..b71e33d 100644 --- a/src/types/position.rs +++ b/src/types/position.rs @@ -1,11 +1,9 @@ -use anyhow::{anyhow, Context, Ok, Result}; +use anyhow::{anyhow, Ok, Result}; use apibara_core::starknet::v1alpha2::FieldElement; use bigdecimal::num_bigint::BigInt; use bigdecimal::{BigDecimal, FromPrimitive}; -use cainome::cairo_serde::{ContractAddress, U256}; use colored::Colorize; use serde::{Deserialize, Serialize}; -use serde_json::Value; use starknet::core::types::{BlockId, BlockTag, FunctionCall}; use starknet::core::types::{Call, Felt}; use starknet::providers::jsonrpc::HttpTransport; @@ -16,9 +14,7 @@ use std::hash::{Hash, Hasher}; use std::sync::Arc; use tokio::sync::RwLock; -use crate::bindings::liquidate::{ - Liquidate, LiquidateParams, PoolKey, RouteNode, Swap, TokenAmount, I129, -}; +use crate::bindings::liquidate::{Liquidate, LiquidateParams, RouteNode, Swap, TokenAmount, I129}; use crate::config::{Config, LiquidationMode, LIQUIDATION_CONFIG_SELECTOR}; use crate::services::oracle::LatestOraclePrices; @@ -26,12 +22,13 @@ use crate::storages::Storage; use crate::utils::apply_overhead; use crate::utils::constants::VESU_RESPONSE_DECIMALS; use crate::utils::conversions::big_decimal_to_felt; +use crate::utils::ekubo::get_ekubo_route; use crate::{types::asset::Asset, utils::conversions::apibara_field_as_felt}; use super::account::StarknetAccount; /// Threshold for which we consider a position almost liquidable. -const ALMOST_LIQUIDABLE_THRESHOLD: f64 = 0.03; +const ALMOST_LIQUIDABLE_THRESHOLD: f64 = 0.02; /// Thread-safe wrapper around the positions. /// PositionsMap is a map between position position_key <=> position. @@ -250,84 +247,6 @@ impl Position { hasher.finish() } - pub async fn get_ekubo_route( - amount_as_string: String, - from_token: String, - to_token: String, - ) -> Result> { - let ekubo_api_endpoint = format!( - "https://mainnet-api.ekubo.org/quote/{amount_as_string}/{from_token}/{to_token}" - ); - let http_client = reqwest::Client::new(); - - let response = http_client.get(ekubo_api_endpoint).send().await?; - - if !response.status().is_success() { - anyhow::bail!("API request failed with status: {}", response.status()); - } - - let response_text = response.text().await?; - - let json_value: Value = serde_json::from_str(&response_text)?; - - // We have to deserialize by hand into a Vec of [RouteNode]. - // TODO: Make this cleaner! - let route = json_value["route"] - .as_array() - .context("'route' is not an array")? - .iter() - .map(|node| { - let pool_key = &node["pool_key"]; - Ok(RouteNode { - pool_key: PoolKey { - token0: ContractAddress(Felt::from_hex( - pool_key["token0"] - .as_str() - .context("token0 is not a string")?, - )?), - token1: ContractAddress(Felt::from_hex( - pool_key["token1"] - .as_str() - .context("token1 is not a string")?, - )?), - fee: u128::from_str_radix( - pool_key["fee"] - .as_str() - .context("fee is not a string")? - .trim_start_matches("0x"), - 16, - ) - .context("Failed to parse fee as u128")?, - tick_spacing: pool_key["tick_spacing"] - .as_u64() - .context("tick_spacing is not a u64")? - as u128, - extension: ContractAddress(Felt::from_hex( - pool_key["extension"] - .as_str() - .context("extension is not a string")?, - )?), - }, - sqrt_ratio_limit: U256::from_bytes_be( - &Felt::from_hex( - node["sqrt_ratio_limit"] - .as_str() - .context("sqrt_ratio_limit is not a string")?, - ) - .unwrap() - .to_bytes_be(), - ), - skip_ahead: node["skip_ahead"] - .as_u64() - .context("skip_ahead is not a u64")? - as u128, - }) - }) - .collect::>>()?; - - Ok(route) - } - /// Returns the TX necessary to liquidate this position (approve + liquidate). // See: https://github.com/vesuxyz/vesu-v1/blob/a2a59936988fcb51bc85f0eeaba9b87cf3777c49/src/singleton.cairo#L1624 pub async fn get_liquidation_txs( @@ -349,19 +268,23 @@ impl Position { }; // As mentionned before the route is inverted for precision purpose - let liquidate_route: Vec = Position::get_ekubo_route( + let liquidate_route: Vec = get_ekubo_route( String::from("10"), // TODO: Investigate the behavior of this value with the Vesu Liquidate contract self.debt.name.clone(), self.collateral.name.clone(), ) .await?; - let withdraw_route: Vec = Position::get_ekubo_route( - String::from("10"), // TODO: Investigate the behavior of this value with the Vesu Liquidate contract - self.debt.name.clone(), - String::from("usdc"), - ) - .await?; + let withdraw_route: Vec = if self.debt.name == "USDC" { + vec![] + } else { + get_ekubo_route( + String::from("10"), // TODO: Investigate the behavior of this value with the Vesu Liquidate contract + self.debt.name.clone(), + String::from("USDC"), + ) + .await? + }; let liquidate_contract = Liquidate::new(liquidate_contract, account.0.clone()); diff --git a/src/utils/ekubo.rs b/src/utils/ekubo.rs new file mode 100644 index 0000000..bfa0dad --- /dev/null +++ b/src/utils/ekubo.rs @@ -0,0 +1,83 @@ +use anyhow::{Context, Ok, Result}; +use cainome::cairo_serde::{ContractAddress, U256}; +use serde_json::Value; +use starknet::core::types::Felt; + +use crate::bindings::liquidate::{PoolKey, RouteNode}; + +pub async fn get_ekubo_route( + amount_as_string: String, + from_token: String, + to_token: String, +) -> Result> { + let ekubo_api_endpoint = + format!("https://mainnet-api.ekubo.org/quote/{amount_as_string}/{from_token}/{to_token}"); + tracing::info!("{}", ekubo_api_endpoint); + let http_client = reqwest::Client::new(); + + let response = http_client.get(ekubo_api_endpoint).send().await?; + + if !response.status().is_success() { + anyhow::bail!("API request failed with status: {}", response.status()); + } + + let response_text = response.text().await?; + + let json_value: Value = serde_json::from_str(&response_text)?; + + // We have to deserialize by hand into a Vec of [RouteNode]. + // TODO: Make this cleaner! + let route = json_value["route"] + .as_array() + .context("'route' is not an array")? + .iter() + .map(|node| { + let pool_key = &node["pool_key"]; + Ok(RouteNode { + pool_key: PoolKey { + token0: ContractAddress(Felt::from_hex( + pool_key["token0"] + .as_str() + .context("token0 is not a string")?, + )?), + token1: ContractAddress(Felt::from_hex( + pool_key["token1"] + .as_str() + .context("token1 is not a string")?, + )?), + fee: u128::from_str_radix( + pool_key["fee"] + .as_str() + .context("fee is not a string")? + .trim_start_matches("0x"), + 16, + ) + .context("Failed to parse fee as u128")?, + tick_spacing: pool_key["tick_spacing"] + .as_u64() + .context("tick_spacing is not a u64")? + as u128, + extension: ContractAddress(Felt::from_hex( + pool_key["extension"] + .as_str() + .context("extension is not a string")?, + )?), + }, + sqrt_ratio_limit: U256::from_bytes_be( + &Felt::from_hex( + node["sqrt_ratio_limit"] + .as_str() + .context("sqrt_ratio_limit is not a string")?, + ) + .unwrap() + .to_bytes_be(), + ), + skip_ahead: node["skip_ahead"] + .as_u64() + .context("skip_ahead is not a u64")? as u128, + }) + }) + .collect::>>()?; + + Ok(route) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 141c7b5..2ceb345 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -9,6 +9,7 @@ use starknet::{ pub mod constants; pub mod conversions; +pub mod ekubo; /// Apply a small overhead of 2% to the provided number. pub fn apply_overhead(num: BigDecimal) -> BigDecimal {