Created
November 28, 2023 01:11
-
-
Save mgild/7d78771cb0d81979154da6c0adc010e0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use tokio; | |
use balancer_sdk; | |
use balancer_sdk::Web3; | |
use web3; | |
use ethcontract; | |
use balancer_sdk::helpers::build_web3; | |
use balancer_sdk::helpers::*; | |
use balancer_sdk::*; | |
use balancer_sdk::{Address, U256}; | |
use balancer_sdk::helpers::*; | |
use std::str::FromStr; | |
use web3::signing::Key; | |
use balancer_sdk::PoolId; | |
use switchboard_utils; | |
use switchboard_utils::Decimal; | |
use futures::future::join_all; | |
use std::future::Future; | |
use web3::futures::TryFutureExt; | |
use web3::futures::FutureExt; | |
use switchboard_utils::FromPrimitive; | |
use switchboard_utils::SbError; | |
use std::pin::Pin; | |
use std::cmp::Ordering; | |
pub const SFRX_ETH: &str = "0xac3e018457b222d93114458476f3e3416abbe38f"; | |
pub const SFRXETH_DECIMALS: u32 = 18; | |
pub const WST_ETH: &str = "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0"; | |
pub const WSTETH_DECIMALS: u32 = 18; | |
pub const WETH: &str = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; | |
pub const WETH_DECIMALS: u32 = 18; | |
pub const USDC: &str = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"; | |
pub const USDC_DECIMALS: u32 = 6; | |
fn u256_to_f64(value: U256) -> f64 { | |
// This function assumes that the value can fit within an f64 | |
// It's a simple way to convert, and more sophisticated methods may be needed for larger values | |
value.as_u128() as f64 | |
} | |
fn get_percentage_of_total(part: U256, total: U256) -> f64 { | |
if total.is_zero() { | |
panic!("Total must not be zero"); | |
} | |
// Convert both U256 values to f64 | |
let part = u256_to_f64(part); | |
let total = u256_to_f64(total); | |
// Calculate the percentage | |
(part / total) * 100.0 | |
} | |
pub async fn fetch_balancer_v2_quote(rpc_url: &str, pool: PoolId, in_token: &str, out_token: &str, amount: U256, slippage: f64) -> Result<U256, u8> { | |
let private_key = PrivateKey::from_str("00e0000a00aaaa0e0a000e0e0000e00e000a000000000000000aaa00a0aaaaaa").unwrap(); | |
let w3 = build_web3(rpc_url); | |
let vault_instance = balancer_sdk::vault::Vault::new(w3); | |
let in_token = addr!(in_token); | |
let out_token = addr!(out_token); | |
let pool_info = vault_instance.get_pool_tokens(pool.into()).call().await.unwrap(); | |
// println!("{:#?}", pool_info); | |
let out_idx = pool_info.0.iter().position(|&x| x == out_token).unwrap(); | |
let in_idx = pool_info.0.iter().position(|&x| x == in_token).unwrap(); | |
let in_pool_total = pool_info.1[in_idx]; | |
let out_pool_total = pool_info.1[out_idx]; | |
let swap_step = BatchSwapStep { | |
pool_id: pool, | |
asset_in_index: in_idx, | |
asset_out_index: out_idx, | |
amount, | |
user_data: UserData("0x").into(), | |
}; | |
let funds = FundManagement { | |
sender: private_key.public_address(), | |
from_internal_balance: false, | |
recipient: private_key.public_address(), | |
to_internal_balance: false, | |
}; | |
let deltas = vault_instance | |
.query_batch_swap( | |
SwapKind::GivenIn as u8, | |
vec![swap_step.into()], | |
pool_info.0, | |
funds.into(), | |
) | |
.from(Account::Offline(private_key, None)) | |
.call() | |
.await | |
.unwrap(); | |
// println!("{}", deltas[out_idx].abs()); | |
let in_percent = get_percentage_of_total(amount, in_pool_total); | |
let out_amount = deltas[out_idx].abs().try_into().unwrap(); | |
let out_percent = get_percentage_of_total(out_amount, out_pool_total); | |
if in_percent >= slippage { | |
println!("IN CHANGE: {}%", in_percent); | |
return Err(0); | |
} | |
if out_percent >= slippage { | |
println!("OUT CHANGE: {}%", out_percent); | |
return Err(0); | |
} | |
Ok(out_amount) | |
} | |
fn median(mut numbers: Vec<Decimal>) -> Option<Decimal> { | |
if numbers.is_empty() { | |
return None; // Cannot find the median of an empty list | |
} | |
// Sort the numbers using `partial_cmp` because `Decimal` doesn't implement `Ord` | |
numbers.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); | |
let mid = numbers.len() / 2; // Find the middle index | |
Some(if numbers.len() % 2 == 0 { | |
// If even number of elements, average the middle two | |
(numbers[mid - 1] + numbers[mid]) / Decimal::from(2) | |
} else { | |
// If odd, return the middle element | |
numbers[mid] | |
}) | |
} | |
#[tokio::main] | |
async fn main() -> Result<(), Box<dyn std::error::Error>> { | |
let SFRXETH_WSTETH_POOL = pool_id!("0x42ed016f826165c2e5976fe5bc3df540c5ad0af700000000000000000000058b"); | |
let WSTETH_WETH_POOL = pool_id!("0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2"); | |
// let WETH_USDC_POOL_UNSAFE = pool_id!("0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019"); | |
// https://ethereumnodes.com/ | |
let rpc_url = "https://rpc.flashbots.net/"; | |
let pool_id = SFRXETH_WSTETH_POOL; | |
let wsteth_amount = fetch_balancer_v2_quote(rpc_url, SFRXETH_WSTETH_POOL, SFRX_ETH, WST_ETH, u256!(10u128.pow(SFRXETH_DECIMALS).to_string()), 0.1).await.unwrap(); | |
let weth_amount = fetch_balancer_v2_quote(rpc_url, WSTETH_WETH_POOL, WST_ETH, WETH, wsteth_amount, 0.1).await.unwrap(); | |
// let usdc_amount = fetch_balancer_v2_quote(rpc_url, WETH_USDC_POOL_UNSAFE, WETH, USDC, weth_amount, 0.1).await?; | |
let v: Vec<Pin<Box<dyn Future<Output = Result<Decimal, SbError>>>>> = vec![ | |
Box::pin(switchboard_utils::exchanges::BitfinexApi::fetch_ticker("tETHUSD", None).map_ok(|x| x.last_price)), | |
Box::pin(switchboard_utils::exchanges::CoinbaseApi::fetch_ticker("ETH-USD", None).map_ok(|x| x.price)), | |
Box::pin(switchboard_utils::exchanges::HuobiApi::fetch_ticker("ethusdt", None).map_ok(|x| Decimal::from_f64(x.close).unwrap())), | |
Box::pin(switchboard_utils::exchanges::KrakenApi::fetch_ticker("ETHUSD", None).map_ok(|x| x.close[0])) | |
]; | |
let eth_prices: Vec<Decimal> = join_all(v).await.into_iter().filter_map(|x| x.ok()).collect(); | |
let eth_price = median(eth_prices).unwrap(); | |
let weth_amount = Decimal::from_i128_with_scale(weth_amount.as_u128().try_into().unwrap(), WETH_DECIMALS); | |
println!("{}", weth_amount * eth_price); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment