Skip to content

Commit

Permalink
fix(smol_stuff): Smol stuff (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
akhercha authored Sep 30, 2024
1 parent 2bf2481 commit d9006d8
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 100 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ data.json
indexer.log
run_indexer.sh
src/bindings

*.json
12 changes: 6 additions & 6 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
4 changes: 2 additions & 2 deletions src/services/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
)
}
}
Expand Down
107 changes: 15 additions & 92 deletions src/types/position.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,22 +14,21 @@ 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;
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.
Expand Down Expand Up @@ -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<Vec<RouteNode>> {
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::<Result<Vec<RouteNode>>>()?;

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(
Expand All @@ -349,19 +268,23 @@ impl Position {
};

// As mentionned before the route is inverted for precision purpose
let liquidate_route: Vec<RouteNode> = Position::get_ekubo_route(
let liquidate_route: Vec<RouteNode> = 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<RouteNode> = 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<RouteNode> = 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());

Expand Down
83 changes: 83 additions & 0 deletions src/utils/ekubo.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<RouteNode>> {
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::<Result<Vec<RouteNode>>>()?;

Ok(route)
}
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit d9006d8

Please sign in to comment.