Skip to content

Commit

Permalink
Simplify stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
DaSichuan committed Mar 7, 2022
1 parent 7b4be89 commit c4b635a
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 70 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions token-lending/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ spl-token = { version = "3.2.0", features=["no-entrypoint"] }
switchboard-program = "0.2.0"
thiserror = "1.0"
uint = "=0.9.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
arrform = "0.1.1"

[dev-dependencies]
assert_matches = "1.5.0"
Expand Down
3 changes: 3 additions & 0 deletions token-lending/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub mod state;
// Export current sdk types for downstream users building with a different sdk version
pub use solana_program;

#[macro_use]
extern crate serde_derive;

solana_program::declare_id!("So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo");

/// Canonical null pubkey. Prints out as "nu11111111111111111111111111111111111111111"
Expand Down
111 changes: 77 additions & 34 deletions token-lending/program/src/logs.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
#![allow(missing_docs)]
use crate::math::Decimal;
use solana_program::{msg, pubkey::Pubkey};
use solana_program::pubkey::Pubkey;
use std::fmt;

#[derive(Debug)]
enum LogEventType {
PythOraclePriceUpdateType,
SwitchboardV1OraclePriceUpdateType,
extern crate serde;
extern crate serde_json;

#[derive(Debug, Serialize)]
pub enum LogEventType {
ProgramVersion,
PythError,
PythOraclePriceUpdate,
SwitchboardError,
SwitchboardV1OraclePriceUpdate,
ReserveStateUpdate,
}

impl fmt::Display for LogEventType {
Expand All @@ -15,49 +22,85 @@ impl fmt::Display for LogEventType {
}
}

pub fn emit_log_event(e: &dyn LogEvent) {
msg!("Solend Log Event");
msg!(&e.to_string());
}

pub trait LogEvent {
fn to_string(&self) -> String;
#[macro_export]
macro_rules! emit_log_event {
($e:expr) => {
msg!("solend-event-log:");
msg!(&serde_json::to_string($e).unwrap());
};
}

#[derive(Serialize)]
pub struct PythOraclePriceUpdate {
pub event_type: LogEventType,
pub oracle_pubkey: Pubkey,
pub price: Decimal,
pub conf: u64,
pub confidence: u64,
pub published_slot: u64,
}

impl LogEvent for PythOraclePriceUpdate {
fn to_string(&self) -> String {
return format!(
"{},{},{},{},{}",
LogEventType::PythOraclePriceUpdateType.to_string(),
self.oracle_pubkey.to_string(),
self.price.to_string(),
self.conf.to_string(),
self.published_slot,
);
}
#[derive(Serialize)]
pub struct PythError {
pub event_type: LogEventType,
pub oracle_pubkey: Pubkey,
pub error_message: String,
}

#[derive(Serialize)]
pub struct SwitchboardV1OraclePriceUpdate {
pub event_type: LogEventType,
pub oracle_pubkey: Pubkey,
pub price: Decimal,
pub published_slot: u64,
}

impl LogEvent for SwitchboardV1OraclePriceUpdate {
fn to_string(&self) -> String {
return format!(
"{},{},{},{}",
LogEventType::SwitchboardV1OraclePriceUpdateType.to_string(),
self.oracle_pubkey.to_string(),
self.price.to_string(),
self.published_slot,
);
}
#[derive(Serialize)]
pub struct SwitchboardError {
pub event_type: LogEventType,
pub oracle_pubkey: Pubkey,
pub error_message: String,
}

#[derive(Serialize)]
pub struct ProgramVersion {
pub event_type: LogEventType,
pub version: u8,
}

#[derive(Serialize)]
pub struct ReserveStateUpdate {
pub event_type: LogEventType,
pub reserve_id: Pubkey,
pub available_amount: u64,
pub borrowed_amount_wads: Decimal,
pub cumulative_borrow_rate_wads: Decimal,
pub collateral_mint_total_supply: u64,
pub collateral_exchange_rate: String,
}

#[derive(Serialize)]
pub struct ObligationStateUpdate {
pub event_type: LogEventType,
pub obligation_id: Pubkey,
pub deposited_value: Decimal,
pub borrowed_value: Decimal,
pub allowed_borrow_value: Decimal,
pub unhealthy_borrow_value: Decimal,
pub deposits: Vec<DepositLog>,
pub borrows: Vec<BorrowLog>,
}

#[derive(Serialize)]
pub struct DepositLog {
pub reserve_id: Pubkey,
pub deposited_amount: u64,
pub market_value: Decimal,
}

#[derive(Serialize)]
pub struct BorrowLog {
pub reserve_id: Pubkey,
pub cumulative_borrow_rate_wads: Decimal,
pub borrowed_amount_wads: Decimal,
pub market_value: Decimal,
}
3 changes: 2 additions & 1 deletion token-lending/program/src/math/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ use uint::construct_uint;

// U192 with 192 bits consisting of 3 x 64-bit words
construct_uint! {
#[derive(Serialize)]
pub struct U192(3);
}

/// Large decimal values, precise to 18 digits
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord, Serialize)]
pub struct Decimal(pub U192);

impl Decimal {
Expand Down
89 changes: 68 additions & 21 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
//! Program state processor
use crate::{
self as spl_token_lending,
self as spl_token_lending, emit_log_event,
error::LendingError,
instruction::LendingInstruction,
logs::{emit_log_event, PythOraclePriceUpdate, SwitchboardV1OraclePriceUpdate},
logs::{
LogEventType, ProgramVersion, PythError, PythOraclePriceUpdate, ReserveStateUpdate,
SwitchboardError, SwitchboardV1OraclePriceUpdate,
},
math::{Decimal, Rate, TryAdd, TryDiv, TryMul, TrySub, WAD},
pyth,
state::{
CalculateBorrowResult, CalculateLiquidationResult, CalculateRepayResult,
InitLendingMarketParams, InitObligationParams, InitReserveParams, LendingMarket,
NewReserveCollateralParams, NewReserveLiquidityParams, Obligation, Reserve,
ReserveCollateral, ReserveConfig, ReserveLiquidity,
ReserveCollateral, ReserveConfig, ReserveLiquidity, PROGRAM_VERSION,
},
};
use num_traits::FromPrimitive;
Expand Down Expand Up @@ -40,6 +43,11 @@ pub fn process_instruction(
accounts: &[AccountInfo],
input: &[u8],
) -> ProgramResult {
emit_log_event!(&ProgramVersion {
event_type: LogEventType::ProgramVersion,
version: PROGRAM_VERSION,
});

let instruction = LendingInstruction::unpack(input)?;
match instruction {
LendingInstruction::InitLendingMarket {
Expand Down Expand Up @@ -432,6 +440,15 @@ fn _refresh_reserve_interest<'a>(

reserve.accrue_interest(clock.slot)?;
reserve.last_update.update_slot(clock.slot);
emit_log_event!(&ReserveStateUpdate {
event_type: LogEventType::ReserveStateUpdate,
reserve_id: *reserve_info.key,
available_amount: reserve.liquidity.available_amount,
borrowed_amount_wads: reserve.liquidity.borrowed_amount_wads,
cumulative_borrow_rate_wads: reserve.liquidity.cumulative_borrow_rate_wads,
collateral_mint_total_supply: reserve.collateral.mint_total_supply,
collateral_exchange_rate: reserve.collateral_exchange_rate()?.to_string(),
});
Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;

Ok(())
Expand Down Expand Up @@ -807,7 +824,6 @@ fn process_refresh_obligation(program_id: &Pubkey, accounts: &[AccountInfo]) ->
let decimals = 10u64
.checked_pow(deposit_reserve.liquidity.mint_decimals as u32)
.ok_or(LendingError::MathOverflow)?;

let market_value = deposit_reserve
.collateral_exchange_rate()?
.decimal_collateral_to_liquidity(collateral.deposited_amount.into())?
Expand Down Expand Up @@ -2153,15 +2169,24 @@ fn get_pyth_price(pyth_price_info: &AccountInfo, clock: &Clock) -> Result<Decima
.map_err(|_| ProgramError::InvalidAccountData)?;

if pyth_price.ptype != pyth::PriceType::Price {
msg!("Oracle price type is invalid {}", pyth_price.ptype as u8);
emit_log_event!(&PythError {
event_type: LogEventType::PythError,
oracle_pubkey: *pyth_price_info.key,
error_message: format!("Oracle price type is invalid: {}", pyth_price.ptype as u8),
});

return Err(LendingError::InvalidOracleConfig.into());
}

if pyth_price.agg.status != pyth::PriceStatus::Trading {
msg!(
"Oracle price status is invalid: {}",
pyth_price.agg.status as u8
);
emit_log_event!(&PythError {
event_type: LogEventType::PythError,
oracle_pubkey: *pyth_price_info.key,
error_message: format!(
"Oracle price status is invalid: {}",
pyth_price.agg.status as u8
),
});
return Err(LendingError::InvalidOracleConfig.into());
}

Expand All @@ -2170,12 +2195,20 @@ fn get_pyth_price(pyth_price_info: &AccountInfo, clock: &Clock) -> Result<Decima
.checked_sub(pyth_price.valid_slot)
.ok_or(LendingError::MathOverflow)?;
if slots_elapsed >= STALE_AFTER_SLOTS_ELAPSED {
msg!("Pyth oracle price is stale");
emit_log_event!(&PythError {
event_type: LogEventType::PythError,
oracle_pubkey: *pyth_price_info.key,
error_message: format!("Pyth oracle price is stale: {} slots old.", slots_elapsed),
});
return Err(LendingError::InvalidOracleConfig.into());
}

let price: u64 = pyth_price.agg.price.try_into().map_err(|_| {
msg!("Oracle price cannot be negative");
emit_log_event!(&PythError {
event_type: LogEventType::PythError,
oracle_pubkey: *pyth_price_info.key,
error_message: "Oracle price cannot be negative".to_string(),
});
LendingError::InvalidOracleConfig
})?;

Expand All @@ -2186,11 +2219,15 @@ fn get_pyth_price(pyth_price_info: &AccountInfo, clock: &Clock) -> Result<Decima
// 100/confidence_ratio = maximum size of confidence range as a percent of price
// confidence_ratio of 10 filters out pyth prices with conf > 10% of price
if conf.checked_mul(confidence_ratio).unwrap() > price {
msg!(
"Oracle price confidence is too wide. price: {}, conf: {}",
price,
conf,
);
emit_log_event!(&PythError {
event_type: LogEventType::PythError,
oracle_pubkey: *pyth_price_info.key,
error_message: format!(
"Oracle price confidence is too wide. price: {}, conf: {}",
price, conf
),
});

return Err(LendingError::InvalidOracleConfig.into());
}

Expand All @@ -2215,10 +2252,11 @@ fn get_pyth_price(pyth_price_info: &AccountInfo, clock: &Clock) -> Result<Decima
.ok_or(LendingError::MathOverflow)?;
Decimal::from(price).try_div(decimals)?
};
emit_log_event(&PythOraclePriceUpdate {
emit_log_event!(&PythOraclePriceUpdate {
event_type: LogEventType::PythOraclePriceUpdate,
oracle_pubkey: *pyth_price_info.key,
price: market_price,
conf: conf,
confidence: conf,
published_slot: pyth_price.valid_slot,
});

Expand All @@ -2238,7 +2276,11 @@ fn get_switchboard_price(
let account_buf = switchboard_feed_info.try_borrow_data()?;
// first byte type discriminator
if account_buf[0] != SwitchboardAccountType::TYPE_AGGREGATOR as u8 {
msg!("switchboard address not of type aggregator");
emit_log_event!(&SwitchboardError {
event_type: LogEventType::SwitchboardError,
oracle_pubkey: *switchboard_feed_info.key,
error_message: "Switchboard feed is not of type aggregator".to_string(),
});
return Err(LendingError::InvalidAccountInput.into());
}

Expand All @@ -2254,7 +2296,11 @@ fn get_switchboard_price(
.checked_sub(open_slot)
.ok_or(LendingError::MathOverflow)?;
if slots_elapsed >= STALE_AFTER_SLOTS_ELAPSED {
msg!("Switchboard oracle price is stale");
emit_log_event!(&SwitchboardError {
event_type: LogEventType::SwitchboardError,
oracle_pubkey: *switchboard_feed_info.key,
error_message: format!("Oracle price is stale by {} slots", slots_elapsed),
});
return Err(LendingError::InvalidOracleConfig.into());
}

Expand All @@ -2266,7 +2312,8 @@ fn get_switchboard_price(
let price = ((price_quotient as f64) * price_float) as u128;

let market_price = Decimal::from(price).try_div(price_quotient)?;
emit_log_event(&SwitchboardV1OraclePriceUpdate {
emit_log_event!(&SwitchboardV1OraclePriceUpdate {
event_type: LogEventType::SwitchboardV1OraclePriceUpdate,
oracle_pubkey: *switchboard_feed_info.key,
price: market_price,
published_slot: open_slot,
Expand Down
Loading

0 comments on commit c4b635a

Please sign in to comment.