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
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;
pub const INITIAL_COLLATERAL_RATIO: u64 = 1;
pub const INITIAL_COLLATERAL_RATE: u64 = INITIAL_COLLATERAL_RATIO * WAD;
pub const PROGRAM_VERSION: u8 = 1;
pub const UNINITIALIZED_VERSION: u8 = 0;
pub const SLOTS_PER_YEAR: u64 =
DEFAULT_TICKS_PER_SECOND / DEFAULT_TICKS_PER_SLOT * SECONDS_PER_DAY * 365;
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)
}
}
}
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];
}
}
}
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)
}
}
}
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;
}
}
}
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
);
}
}