Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Implement fallback oracles for perps #943

Open
wants to merge 23 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f54bb6f
Serge/liquidator split tcs and liquidation (#914)
farnyser Mar 20, 2024
ceeac9d
service-mango-health: use jemalloc (#922)
farnyser Mar 27, 2024
e3a7ed9
liquidator: randomly select token/perps for rebalancing to avoid fail…
farnyser Mar 27, 2024
0b7e62e
liquidator: do not panic if token or perp rebalancing fails (#927)
farnyser Apr 1, 2024
2520c7d
liquidator: forcefully exit process if snapshot job die (#924)
farnyser Apr 1, 2024
e38798e
liquidator: add a sequence check in rebalancing (#926)
farnyser Apr 3, 2024
55105e0
rust client: add a CU estimate for token withdrawal (#934)
farnyser Apr 8, 2024
9df73a0
Audit: Remove unused fn, add comments (#935)
ckamm Apr 8, 2024
653cf9f
token_withdraw: avoid silencing errors with is_ok() (#936)
ckamm Apr 8, 2024
01d5237
Liquidator: add Sanctum swap (#919)
farnyser Apr 10, 2024
75a07e9
program: remove delegate account withdrawal limit (#939)
farnyser Apr 10, 2024
d0125e9
liquidator: rebalance with openbook (limit order) (#938)
farnyser Apr 10, 2024
fe86295
program; fix health check ix gate (#940)
farnyser Apr 11, 2024
ccc479b
add audit report for v0.24.0 (#941)
thibaultosec Apr 15, 2024
4c3814c
Changelog for v0.24.0 (#942)
farnyser Apr 15, 2024
5a37232
add fallback_oracle to PerpMarket
Lou-Kamades Apr 15, 2024
c9fffda
perp markets can use fallback oracles from built health accounts
Lou-Kamades Apr 15, 2024
d34a5e2
test: add test_liq_perps_force_cancel_stale_oracle
Lou-Kamades Apr 15, 2024
f5913cd
ensure account_retriever uses perp fallbacks
Lou-Kamades Apr 15, 2024
2170f70
perp_place_order uses fallbacks
Lou-Kamades Apr 15, 2024
659e7fa
add fallbacks to perp_liq_negative_pnl_or_bankruptcy
Lou-Kamades Apr 16, 2024
22af31c
add fallbacks to perp_force_close_position
Lou-Kamades Apr 16, 2024
20eef53
implement fallbacks for perp_liq_base_or_positive_pnl
Lou-Kamades Apr 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,33 @@ Update this for each program release and mainnet deployment.

## not on mainnet

### v0.23.0, 2024-3-
### v0.24.0, 2024-4-

- Allow skipping banks and invalid oracles when computing health (#891)

This is only possible when we know for sure that the operation would not put the account into negative health zone.

- Add support for Raydium CLMM as oracle fallback (#856)

- Add a `TokenBalanceLog` when charging collateral fees (#894)

- Withdraw instruction: remove overflow error and return appropriate error message instead (#910)

- Banks: add more safety checks (#895)

- Add a health check instruction (#913)

Assert in a transaction that operation run on a mango account does not reduce it's health below a specified amount.

- Add a sequence check instruction (#909)

Assert that a transaction was emitted and run with a correct view of the current mango state.

## mainnet

### v0.23.0, 2024-3-8

Deployment: Mar 8, 2024 at 12:10:52 Central European Standard Time, https://explorer.solana.com/tx/6MXGookZoYGMYb7tWrrmgZzVA13HJimHNqwHRVFeqL9YpQD7YasH1pQn4MSQTK1o13ixKTGFxwZsviUzmHzzP9m

- Allow disabling asset liquidations for tokens (#867)

Expand All @@ -26,8 +52,6 @@ Update this for each program release and mainnet deployment.
- Flash loan: Add a "swap without flash loan fees" option (#882)
- Cleanup, tests and minor (#878, #875, #854, #838, #895)

## mainnet

### v0.22.0, 2024-3-3

Deployment: Mar 3, 2024 at 23:52:08 Central European Standard Time, https://explorer.solana.com/tx/3MpEMU12Pv7RpSnwfShoM9sbyr41KAEeJFCVx9ypkq8nuK8Q5vm7CRLkdhH3u91yQ4k44a32armZHaoYguX6NqsY
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added audits/Audit_OtterSec_Mango_v0.24.0.pdf
Binary file not shown.
39 changes: 39 additions & 0 deletions bin/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,31 @@ struct JupiterSwap {
rpc: Rpc,
}

#[derive(Args, Debug, Clone)]
struct SanctumSwap {
#[clap(long)]
account: String,

/// also pays for everything
#[clap(short, long)]
owner: String,

#[clap(long)]
input_mint: String,

#[clap(long)]
output_mint: String,

#[clap(short, long)]
amount: u64,

#[clap(short, long, default_value = "50")]
max_slippage_bps: u64,

#[clap(flatten)]
rpc: Rpc,
}

#[derive(ArgEnum, Clone, Debug)]
#[repr(u8)]
pub enum CliSide {
Expand Down Expand Up @@ -189,6 +214,7 @@ enum Command {
CreateAccount(CreateAccount),
Deposit(Deposit),
JupiterSwap(JupiterSwap),
SanctumSwap(SanctumSwap),
GroupAddress {
#[clap(short, long)]
creator: String,
Expand Down Expand Up @@ -312,6 +338,19 @@ async fn main() -> Result<(), anyhow::Error> {
.await?;
println!("{}", txsig);
}
Command::SanctumSwap(cmd) => {
let client = cmd.rpc.client(Some(&cmd.owner))?;
let account = pubkey_from_cli(&cmd.account);
let owner = Arc::new(keypair_from_cli(&cmd.owner));
let input_mint = pubkey_from_cli(&cmd.input_mint);
let output_mint = pubkey_from_cli(&cmd.output_mint);
let client = MangoClient::new_for_existing_account(client, account, owner).await?;
let txsig = client
.sanctum()
.swap(input_mint, output_mint, cmd.max_slippage_bps, cmd.amount)
.await?;
println!("{}", txsig);
}
Command::GroupAddress { creator, num } => {
let creator = pubkey_from_cli(&creator);
println!("{}", MangoClient::group_for_admin(creator, num));
Expand Down
7 changes: 6 additions & 1 deletion bin/cli/src/save_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub async fn save_snapshot(
.await?;

// Getting solana account snapshots via jsonrpc
snapshot_source::start(
let snapshot_job = snapshot_source::start(
snapshot_source::Config {
rpc_http_url: rpc_url.clone(),
mango_group,
Expand All @@ -79,6 +79,11 @@ pub async fn save_snapshot(
extra_accounts,
account_update_sender,
);
tokio::spawn(async move {
let res = snapshot_job.await;
tracing::error!("Snapshot job exited, terminating process.. ({:?})", res);
std::process::exit(-1);
});

let mut chain_data = chain_data::ChainData::new();

Expand Down
5 changes: 4 additions & 1 deletion bin/liquidator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ shellexpand = "2.1.0"
solana-account-decoder = { workspace = true }
solana-client = { workspace = true }
solana-logger = { workspace = true }
solana-address-lookup-table-program = "~1.16.7"
solana-rpc = { workspace = true }
solana-sdk = { workspace = true }
tokio = { version = "1", features = ["full"] }
tokio-stream = { version = "0.1.9"}
tokio-tungstenite = "0.16.1"
tracing = "0.1"
regex = "1.9.5"
hdrhistogram = "7.5.4"
hdrhistogram = "7.5.4"
indexmap = "2.0.0"
borsh = { version = "0.10.3", features = ["const-generics"] }
53 changes: 49 additions & 4 deletions bin/liquidator/src/cli_args.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::trigger_tcs;
use anchor_lang::prelude::Pubkey;
use clap::Parser;
use mango_v4_client::{jupiter, priority_fees_cli};
use mango_v4_client::{priority_fees_cli, swap};
use std::collections::HashSet;

#[derive(Parser, Debug)]
Expand All @@ -28,11 +28,11 @@ pub(crate) enum JupiterVersionArg {
V6,
}

impl From<JupiterVersionArg> for jupiter::Version {
impl From<JupiterVersionArg> for swap::Version {
fn from(a: JupiterVersionArg) -> Self {
match a {
JupiterVersionArg::Mock => jupiter::Version::Mock,
JupiterVersionArg::V6 => jupiter::Version::V6,
JupiterVersionArg::Mock => swap::Version::Mock,
JupiterVersionArg::V6 => swap::Version::V6,
}
}
}
Expand Down Expand Up @@ -121,6 +121,12 @@ pub struct Cli {
#[clap(long, env, value_parser, value_delimiter = ',')]
pub(crate) rebalance_alternate_jupiter_route_tokens: Option<Vec<u16>>,

/// query sanctum for routes to and from these tokens
///
/// These routes will only be used when trying to rebalance a LST token
#[clap(long, env, value_parser, value_delimiter = ',')]
pub(crate) rebalance_alternate_sanctum_route_tokens: Option<Vec<u16>>,

/// When closing borrows, the rebalancer can't close token positions exactly.
/// Instead it purchases too much and then gets rid of the excess in a second step.
/// If this is 0.05, then it'll swap borrow_value * (1 + 0.05) quote token into borrow token.
Expand All @@ -130,12 +136,25 @@ pub struct Cli {
#[clap(long, env, default_value = "30")]
pub(crate) rebalance_refresh_timeout_secs: u64,

#[clap(long, env, value_enum, default_value = "false")]
pub(crate) rebalance_using_limit_order: BoolArg,

/// distance (in bps) from oracle price at which to place order for rebalancing
#[clap(long, env, default_value = "100")]
pub(crate) rebalance_limit_order_distance_from_oracle_price_bps: u64,

/// if taking tcs orders is enabled
///
/// typically only disabled for tests where swaps are unavailable
#[clap(long, env, value_enum, default_value = "true")]
pub(crate) take_tcs: BoolArg,

#[clap(long, env, default_value = "30")]
pub(crate) tcs_refresh_timeout_secs: u64,

#[clap(long, env, default_value = "1000")]
pub(crate) tcs_check_interval_ms: u64,

/// profit margin at which to take tcs orders
#[clap(long, env, default_value = "0.0005")]
pub(crate) tcs_profit_fraction: f64,
Expand Down Expand Up @@ -178,6 +197,10 @@ pub struct Cli {
#[clap(long, env, default_value = "https://quote-api.jup.ag/v6")]
pub(crate) jupiter_v6_url: String,

/// override the jupiter http request timeout
#[clap(long, env, default_value = "30")]
pub(crate) jupiter_timeout_secs: u64,

/// provide a jupiter token, currently only for jup v6
#[clap(long, env, default_value = "")]
pub(crate) jupiter_token: String,
Expand All @@ -191,6 +214,12 @@ pub struct Cli {
#[clap(long, env, value_enum, default_value = "true")]
pub(crate) telemetry: BoolArg,

/// if liquidation is enabled
///
/// might be used to run an instance of liquidator dedicated to TCS and another one for liquidation
#[clap(long, env, value_enum, default_value = "true")]
pub(crate) liquidation_enabled: BoolArg,

/// liquidation refresh timeout in secs
#[clap(long, env, default_value = "30")]
pub(crate) liquidation_refresh_timeout_secs: u8,
Expand All @@ -216,4 +245,20 @@ pub struct Cli {
/// how long should it wait before logging an oracle error again (for the same token)
#[clap(long, env, default_value = "30")]
pub(crate) skip_oracle_error_in_logs_duration_secs: u64,

/// max number of liquidation/tcs to do concurrently
#[clap(long, env, default_value = "5")]
pub(crate) max_parallel_operations: u64,

/// Also use sanctum for rebalancing
#[clap(long, env, value_enum, default_value = "false")]
pub(crate) sanctum_enabled: BoolArg,

/// override the url to sanctum
#[clap(long, env, default_value = "https://api.sanctum.so/v1")]
pub(crate) sanctum_url: String,

/// override the sanctum http request timeout
#[clap(long, env, default_value = "30")]
pub(crate) sanctum_timeout_secs: u64,
}
47 changes: 43 additions & 4 deletions bin/liquidator/src/liquidate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use mango_v4_client::{chain_data, MangoClient, PreparedInstructions};
use solana_sdk::signature::Signature;

use futures::{stream, StreamExt, TryStreamExt};
use mango_v4::accounts_ix::HealthCheckKind::MaintRatio;
use rand::seq::SliceRandom;
use tracing::*;
use {anyhow::Context, fixed::types::I80F48, solana_sdk::pubkey::Pubkey};
Expand Down Expand Up @@ -260,7 +261,22 @@ impl<'a> LiquidateHelper<'a> {
)
.await
.context("creating perp_liq_base_or_positive_pnl_instruction")?;

liq_ixs.cu = liq_ixs.cu.max(self.config.compute_limit_for_liq_ix);

let liqor = &self.client.mango_account().await?;
liq_ixs.append(
self.client
.health_check_instruction(
liqor,
self.config.min_health_ratio,
vec![],
vec![*perp_market_index],
MaintRatio,
)
.await?,
);

let txsig = self
.client
.send_and_confirm_owner_tx(liq_ixs.to_instructions())
Expand Down Expand Up @@ -501,6 +517,20 @@ impl<'a> LiquidateHelper<'a> {
.await
.context("creating liq_token_with_token ix")?;
liq_ixs.cu = liq_ixs.cu.max(self.config.compute_limit_for_liq_ix);

let liqor = self.client.mango_account().await?;
liq_ixs.append(
self.client
.health_check_instruction(
&liqor,
self.config.min_health_ratio,
vec![asset_token_index, liab_token_index],
vec![],
MaintRatio,
)
.await?,
);

let txsig = self
.client
.send_and_confirm_owner_tx(liq_ixs.to_instructions())
Expand Down Expand Up @@ -651,14 +681,11 @@ impl<'a> LiquidateHelper<'a> {
}

#[allow(clippy::too_many_arguments)]
pub async fn maybe_liquidate_account(
pub async fn can_liquidate_account(
mango_client: &MangoClient,
account_fetcher: &chain_data::AccountFetcher,
pubkey: &Pubkey,
config: &Config,
) -> anyhow::Result<bool> {
let liqor_min_health_ratio = I80F48::from_num(config.min_health_ratio);

let account = account_fetcher.fetch_mango_account(pubkey)?;
let health_cache = mango_client
.health_cache(&account)
Expand All @@ -675,6 +702,18 @@ pub async fn maybe_liquidate_account(
"possible candidate",
);

Ok(true)
}

#[allow(clippy::too_many_arguments)]
pub async fn maybe_liquidate_account(
mango_client: &MangoClient,
account_fetcher: &chain_data::AccountFetcher,
pubkey: &Pubkey,
config: &Config,
) -> anyhow::Result<bool> {
let liqor_min_health_ratio = I80F48::from_num(config.min_health_ratio);

// Fetch a fresh account and re-compute
// This is -- unfortunately -- needed because the websocket streams seem to not
// be great at providing timely updates to the account data.
Expand Down
Loading
Loading