1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! State types

use std::convert::TryInto;

use arrayref::{array_refs, mut_array_refs};
use solana_program::program_option::COption;
use solana_program::pubkey::{Pubkey, PUBKEY_BYTES};
use solana_program::{
    clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, SECONDS_PER_DAY},
    msg,
    program_error::ProgramError,
};

pub use last_update::*;
pub use lending_market::*;
pub use obligation::*;
pub use reserve::*;

use solana_maths::{Decimal, WAD};

mod last_update;
mod lending_market;
mod obligation;
mod reserve;

/// Collateral tokens are initially valued at a ratio of 1:1 (collateral:liquidity)
pub const INITIAL_COLLATERAL_RATIO: u64 = 1;

/// Collateral tokens are initially valued at a ratio of 1:1 RATE(collateral:liquidity)
pub const INITIAL_COLLATERAL_RATE: u64 = INITIAL_COLLATERAL_RATIO * WAD;

/// Current version of the program and all new accounts created
pub const PROGRAM_VERSION: u8 = 1;

/// Accounts are created with data zeroed out, so uninitialized state instances
/// will have the version set to 0.
pub const UNINITIALIZED_VERSION: u8 = 0;

/// Number of slots per year
pub const SLOTS_PER_YEAR: u64 =
    DEFAULT_TICKS_PER_SECOND / DEFAULT_TICKS_PER_SLOT * SECONDS_PER_DAY * 365;

// Helpers
fn pack_decimal(decimal: Decimal, dst: &mut [u8; 16]) {
    *dst = decimal
        .to_scaled_val()
        .expect("Decimal cannot be packed")
        .to_le_bytes();
}

fn unpack_decimal(src: &[u8; 16]) -> Decimal {
    Decimal::from_scaled_val(u128::from_le_bytes(*src))
}

fn pack_bool(boolean: bool, dst: &mut [u8; 1]) {
    *dst = (boolean as u8).to_le_bytes()
}

fn unpack_bool(src: &[u8; 1]) -> Result<bool, ProgramError> {
    match u8::from_le_bytes(*src) {
        0 => Ok(false),
        1 => Ok(true),
        _ => {
            msg!("Boolean cannot be unpacked");
            Err(ProgramError::InvalidAccountData)
        }
    }
}

///use pack coption key compact
fn pack_coption_key(src: &COption<Pubkey>, dst: &mut [u8; 4 + PUBKEY_BYTES]) {
    #[allow(clippy::ptr_offset_with_cast)]
    let (tag, body) = mut_array_refs![dst, 4, PUBKEY_BYTES];
    match src {
        COption::Some(key) => {
            *tag = [1, 0, 0, 0];
            body.copy_from_slice(key.as_ref());
        }
        COption::None => {
            *tag = [0; 4];
        }
    }
}

///use unpack coption key compact
fn unpack_coption_key(src: &[u8; 4 + PUBKEY_BYTES]) -> Result<COption<Pubkey>, ProgramError> {
    #[allow(clippy::ptr_offset_with_cast)]
    let (tag, body) = array_refs![src, 4, PUBKEY_BYTES];
    match *tag {
        [0, 0, 0, 0] => Ok(COption::None),
        [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))),
        _ => {
            msg!("COption<Pubkey> cannot be unpacked");
            Err(ProgramError::InvalidAccountData)
        }
    }
}

///pack coption of pubkey into buffer
pub fn pack_coption_key_compact(src: &COption<Pubkey>, dst: &mut [u8; 1 + PUBKEY_BYTES]) {
    match src {
        COption::Some(key) => {
            dst[0] = 1;
            dst[1..].copy_from_slice(key.as_ref());
        }
        COption::None => {
            dst[0] = 0;
        }
    }
}

///unpack coption pubkey from buffer
pub fn unpack_coption_key_compact(
    src: &[u8; 1 + PUBKEY_BYTES],
) -> Result<COption<Pubkey>, ProgramError> {
    match src[0] {
        0 => Ok(COption::None),
        1 => Ok(COption::Some(Pubkey::new_from_array(
            src[1..].try_into().unwrap(),
        ))),
        _ => {
            msg!("COption<Pubkey> cannot be unpacked");
            Err(ProgramError::InvalidAccountData)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn initial_collateral_rate_sanity() {
        assert_eq!(
            INITIAL_COLLATERAL_RATIO.checked_mul(WAD).unwrap(),
            INITIAL_COLLATERAL_RATE
        );
    }
}