Skip to content

Jito rewards calculations #170

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

Open
wants to merge 3 commits into
base: upcoming
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,9 @@ fn _refresh_reserve<'a>(

/// Lite version of refresh_reserve that should be used when the oracle price doesn't need to be updated
/// BE CAREFUL WHEN USING THIS
fn _refresh_reserve_interest<'a>(
fn _refresh_reserve_interest(
program_id: &Pubkey,
reserve_info: &AccountInfo<'a>,
reserve_info: &AccountInfo<'_>,
clock: &Clock,
) -> ProgramResult {
let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
Expand Down
2 changes: 1 addition & 1 deletion token-lending/program/tests/helpers/mock_pyth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl Processor {
msg!("Mock Pyth: Set price");
let price_account_info = next_account_info(account_info_iter)?;
let data = &mut price_account_info.try_borrow_mut_data()?;
let mut price_account: &mut PriceAccount = load_mut(data).unwrap();
let price_account: &mut PriceAccount = load_mut(data).unwrap();

price_account.agg.price = price;
price_account.agg.conf = conf;
Expand Down
1 change: 1 addition & 0 deletions token-lending/sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ num-derive = "0.3"
num-traits = "0.2"
pyth-sdk-solana = "0.7.0"
solana-program = ">=1.9, < 1.15"
solana-client = ">=1.9, < 1.15"
spl-token = { version = "3.2.0", features=["no-entrypoint"] }
static_assertions = "1.1.0"
thiserror = "1.0"
Expand Down
73 changes: 73 additions & 0 deletions token-lending/sdk/examples/jito.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey;
use std::collections::HashMap;

use solend_sdk::{
offchain_utils::{
get_solend_accounts_as_map, offchain_refresh_obligation, offchain_refresh_reserve_interest,
},
solend_mainnet,
};

#[derive(Debug, Clone, Default)]
struct Position {
pub deposit_balance: u64,
pub borrow_balance: u64,
}

pub fn main() {
let rpc_url = std::env::var("RPC_URL")
.unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string());
let rpc_client = RpcClient::new(rpc_url);

let mut accounts = get_solend_accounts_as_map(&solend_mainnet::id(), &rpc_client).unwrap();

// update solend-specific interest variables
let slot = rpc_client.get_slot().unwrap();
for reserve in accounts.reserves.values_mut() {
let _ = offchain_refresh_reserve_interest(reserve, slot);
}

for obligation in accounts.obligations.values_mut() {
offchain_refresh_obligation(obligation, &accounts.reserves).unwrap();
}

// calculate jitosol balances per user across all pools
let jitosol = pubkey!("J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn");
let mut user_to_position = HashMap::new();

for obligation in accounts.obligations.values() {
for deposit in &obligation.deposits {
let deposit_reserve = accounts.reserves.get(&deposit.deposit_reserve).unwrap();
if deposit_reserve.liquidity.mint_pubkey == jitosol {
let position = user_to_position
.entry(obligation.owner)
.or_insert(Position::default());

// convert cJitoSol to JitoSol
let cjitosol_deposited = deposit.deposited_amount;
let jitosol_deposited = deposit_reserve
.collateral_exchange_rate()
.unwrap()
.collateral_to_liquidity(cjitosol_deposited)
.unwrap();

position.deposit_balance += jitosol_deposited;
}
}

for borrow in &obligation.borrows {
let borrow_reserve = accounts.reserves.get(&borrow.borrow_reserve).unwrap();
if borrow_reserve.liquidity.mint_pubkey == jitosol {
let position = user_to_position
.entry(obligation.owner)
.or_insert(Position::default());

position.borrow_balance += borrow.borrowed_amount_wads.try_round_u64().unwrap();
}
}
}

println!("Done refreshing");
println!("{:#?}", user_to_position);
}
1 change: 1 addition & 0 deletions token-lending/sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
pub mod error;
pub mod instruction;
pub mod math;
pub mod offchain_utils;
pub mod oracles;
pub mod state;

Expand Down
178 changes: 178 additions & 0 deletions token-lending/sdk/src/offchain_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#![allow(missing_docs)]

use solana_client::rpc_client::RpcClient;
use solana_program::slot_history::Slot;
// use pyth_sdk_solana;
use solana_program::program_error::ProgramError;
use std::result::Result;

use crate::{state::LastUpdate, NULL_PUBKEY};

use solana_program::{program_pack::Pack, pubkey::Pubkey};

use crate::math::{Decimal, Rate, TryAdd, TryMul};

use crate::state::{LendingMarket, Obligation, Reserve};
use std::{collections::HashMap, error::Error};

#[derive(Debug, Clone)]

Check warning on line 18 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L18

Added line #L18 was not covered by tests
pub struct SolendAccounts {
pub lending_markets: HashMap<Pubkey, LendingMarket>,
pub reserves: HashMap<Pubkey, Reserve>,
pub obligations: HashMap<Pubkey, Obligation>,
}

pub fn get_solend_accounts_as_map(
lending_program_id: &Pubkey,
client: &RpcClient,
) -> Result<SolendAccounts, Box<dyn Error>> {
let accounts = client.get_program_accounts(lending_program_id)?;

Check warning on line 29 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L25-L29

Added lines #L25 - L29 were not covered by tests

let (lending_markets, reserves, obligations) = accounts.into_iter().fold(
(HashMap::new(), HashMap::new(), HashMap::new()),
|(mut lending_markets, mut reserves, mut obligations), (pubkey, account)| {
match account.data.len() {

Check warning on line 34 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L31-L34

Added lines #L31 - L34 were not covered by tests
Obligation::LEN => {
if let Ok(o) = Obligation::unpack(&account.data) {
obligations.insert(pubkey, o);
}

Check warning on line 38 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L36-L38

Added lines #L36 - L38 were not covered by tests
}
Reserve::LEN => {
if let Ok(r) = Reserve::unpack(&account.data) {
reserves.insert(pubkey, r);
}

Check warning on line 43 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L41-L43

Added lines #L41 - L43 were not covered by tests
}
LendingMarket::LEN => {
if let Ok(l) = LendingMarket::unpack(&account.data) {
lending_markets.insert(pubkey, l);
}

Check warning on line 48 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L46-L48

Added lines #L46 - L48 were not covered by tests
}
_ => (),

Check warning on line 50 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L50

Added line #L50 was not covered by tests
};
(lending_markets, reserves, obligations)
},
);

Ok(SolendAccounts {
lending_markets,
reserves,
obligations,
})
}

Check warning on line 61 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L52-L61

Added lines #L52 - L61 were not covered by tests

pub fn offchain_refresh_reserve_interest(
reserve: &mut Reserve,
slot: Slot,
) -> Result<(), Box<dyn Error>> {
reserve.accrue_interest(slot)?;
reserve.last_update = LastUpdate { slot, stale: false };

Ok(())
}

Check warning on line 71 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L67-L71

Added lines #L67 - L71 were not covered by tests

pub fn offchain_refresh_reserve(
_pubkey: &Pubkey,
reserve: &mut Reserve,
slot: Slot,
prices: &HashMap<Pubkey, Option<Decimal>>,
) -> Result<(), Box<dyn Error>> {
let pyth_oracle = reserve.liquidity.pyth_oracle_pubkey;
let switchboard_oracle = reserve.liquidity.switchboard_oracle_pubkey;

Check warning on line 80 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L73-L80

Added lines #L73 - L80 were not covered by tests

let price = if let Some(Some(price)) = prices.get(&pyth_oracle) {
if pyth_oracle != NULL_PUBKEY {
Some(*price)

Check warning on line 84 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L82-L84

Added lines #L82 - L84 were not covered by tests
} else {
None

Check warning on line 86 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L86

Added line #L86 was not covered by tests
}
} else if let Some(Some(price)) = prices.get(&switchboard_oracle) {
if switchboard_oracle != NULL_PUBKEY {
Some(*price)

Check warning on line 90 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L88-L90

Added lines #L88 - L90 were not covered by tests
} else {
None

Check warning on line 92 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L92

Added line #L92 was not covered by tests
}
} else {
None

Check warning on line 95 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L95

Added line #L95 was not covered by tests
};

if let Some(price) = price {
reserve.liquidity.market_price = price;
} else {
return Err("No price".into());

Check warning on line 101 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L98-L101

Added lines #L98 - L101 were not covered by tests
}

reserve.accrue_interest(slot)?;
reserve.last_update = LastUpdate { slot, stale: false };

Ok(())
}

Check warning on line 108 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L104-L108

Added lines #L104 - L108 were not covered by tests

pub fn offchain_refresh_obligation(
o: &mut Obligation,
reserves: &HashMap<Pubkey, Reserve>,
) -> Result<(), Box<dyn Error>> {
o.deposited_value = Decimal::zero();
o.super_unhealthy_borrow_value = Decimal::zero();
o.unhealthy_borrow_value = Decimal::zero();
o.borrowed_value = Decimal::zero();

Check warning on line 117 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L110-L117

Added lines #L110 - L117 were not covered by tests

for collateral in &mut o.deposits {
let deposit_reserve = reserves
.get(&collateral.deposit_reserve)
.ok_or(ProgramError::Custom(35))?;

Check warning on line 122 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L119-L122

Added lines #L119 - L122 were not covered by tests

let liquidity_amount = deposit_reserve
.collateral_exchange_rate()?
.decimal_collateral_to_liquidity(collateral.deposited_amount.into())?;

Check warning on line 126 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L124-L126

Added lines #L124 - L126 were not covered by tests

let market_value = deposit_reserve.market_value(liquidity_amount)?;
let liquidation_threshold_rate =
Rate::from_percent(deposit_reserve.config.liquidation_threshold);
let max_liquidation_threshold_rate =
Rate::from_percent(deposit_reserve.config.max_liquidation_threshold);

collateral.market_value = market_value;

o.deposited_value = o.deposited_value.try_add(market_value)?;
o.unhealthy_borrow_value = o
.unhealthy_borrow_value
.try_add(market_value.try_mul(liquidation_threshold_rate)?)?;
o.super_unhealthy_borrow_value = o
.super_unhealthy_borrow_value
.try_add(market_value.try_mul(max_liquidation_threshold_rate)?)?;

Check warning on line 142 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L128-L142

Added lines #L128 - L142 were not covered by tests
}

let mut max_borrow_weight = None;

Check warning on line 145 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L145

Added line #L145 was not covered by tests

for (index, liquidity) in o.borrows.iter_mut().enumerate() {
let borrow_reserve = reserves.get(&liquidity.borrow_reserve).unwrap();
liquidity.accrue_interest(borrow_reserve.liquidity.cumulative_borrow_rate_wads)?;

Check warning on line 149 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L147-L149

Added lines #L147 - L149 were not covered by tests

let market_value = borrow_reserve.market_value(liquidity.borrowed_amount_wads)?;
liquidity.market_value = market_value;

o.borrowed_value = o
.borrowed_value
.try_add(market_value.try_mul(borrow_reserve.borrow_weight())?)?;

Check warning on line 156 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L151-L156

Added lines #L151 - L156 were not covered by tests

let borrow_weight_and_pubkey = (
borrow_reserve.config.added_borrow_weight_bps,
borrow_reserve.liquidity.mint_pubkey,
);

max_borrow_weight = match max_borrow_weight {
None => Some((borrow_weight_and_pubkey, index)),
Some((max_borrow_weight_and_pubkey, _)) => {
if liquidity.borrowed_amount_wads > Decimal::zero()
&& borrow_weight_and_pubkey > max_borrow_weight_and_pubkey

Check warning on line 167 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L158-L167

Added lines #L158 - L167 were not covered by tests
{
Some((borrow_weight_and_pubkey, index))

Check warning on line 169 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L169

Added line #L169 was not covered by tests
} else {
max_borrow_weight

Check warning on line 171 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L171

Added line #L171 was not covered by tests
}
}
};
}

Ok(())
}

Check warning on line 178 in token-lending/sdk/src/offchain_utils.rs

View check run for this annotation

Codecov / codecov/patch

token-lending/sdk/src/offchain_utils.rs#L177-L178

Added lines #L177 - L178 were not covered by tests