klend_interface/state/
mod.rs1mod common;
2mod global_config;
3mod lending_market;
4mod obligation;
5mod referral;
6mod reserve;
7mod withdraw_ticket;
8
9pub use common::*;
10pub use global_config::*;
11pub use lending_market::*;
12pub use obligation::*;
13pub use referral::*;
14pub use reserve::*;
15pub use spl_discriminator::{ArrayDiscriminator, SplDiscriminate};
16pub use withdraw_ticket::*;
17
18pub const DISCRIMINATOR_SIZE: usize = ArrayDiscriminator::LENGTH;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum AccountDataError {
24 DataTooShort { expected: usize, actual: usize },
26 InvalidDiscriminator { expected: [u8; 8], actual: [u8; 8] },
28 AlignmentError,
30}
31
32impl core::fmt::Display for AccountDataError {
33 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34 match self {
35 Self::DataTooShort { expected, actual } => {
36 write!(
37 f,
38 "account data too short: expected {expected}, got {actual}"
39 )
40 }
41 Self::InvalidDiscriminator { expected, actual } => {
42 write!(
43 f,
44 "invalid discriminator: expected {expected:?}, got {actual:?}"
45 )
46 }
47 Self::AlignmentError => {
48 write!(
49 f,
50 "account data is not properly aligned for the target type"
51 )
52 }
53 }
54 }
55}
56
57impl std::error::Error for AccountDataError {}
58
59pub fn from_account_data<T: bytemuck::Pod + SplDiscriminate>(
63 data: &[u8],
64) -> Result<&T, AccountDataError> {
65 let expected_len = DISCRIMINATOR_SIZE + core::mem::size_of::<T>();
66 if data.len() < expected_len {
67 return Err(AccountDataError::DataTooShort {
68 expected: expected_len,
69 actual: data.len(),
70 });
71 }
72 let disc = &data[..DISCRIMINATOR_SIZE];
73 if disc != T::SPL_DISCRIMINATOR_SLICE {
74 let mut actual = [0u8; 8];
75 actual.copy_from_slice(disc);
76 let mut expected = [0u8; 8];
77 expected.copy_from_slice(T::SPL_DISCRIMINATOR_SLICE);
78 return Err(AccountDataError::InvalidDiscriminator { expected, actual });
79 }
80 bytemuck::try_from_bytes(&data[DISCRIMINATOR_SIZE..expected_len])
81 .map_err(|_| AccountDataError::AlignmentError)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn verify_account_sizes() {
90 assert_eq!(core::mem::size_of::<Obligation>(), 3336);
91 assert_eq!(core::mem::size_of::<Reserve>(), 8616);
92 assert_eq!(core::mem::size_of::<ReserveConfig>(), 936);
93 assert_eq!(core::mem::size_of::<TokenInfo>(), 384);
94 assert_eq!(core::mem::size_of::<LendingMarket>(), 4656);
95 assert_eq!(core::mem::size_of::<GlobalConfig>(), 1024);
96 assert_eq!(core::mem::size_of::<ReferrerTokenState>(), 352);
97 assert_eq!(core::mem::size_of::<UserMetadata>(), 1024);
98 assert_eq!(core::mem::size_of::<ReferrerState>(), 64);
99 assert_eq!(core::mem::size_of::<WithdrawTicket>(), 512);
100 }
101
102 #[test]
103 fn verify_account_discriminators() {
104 use sha2::{Digest, Sha256};
108
109 macro_rules! check {
110 ($ty:ty, $name:expr) => {{
111 let mut h = Sha256::new();
112 h.update(concat!("account:", $name).as_bytes());
113 let hash = h.finalize();
114 let mut expected = [0u8; 8];
115 expected.copy_from_slice(&hash[..8]);
116 assert_eq!(
117 <$ty as SplDiscriminate>::SPL_DISCRIMINATOR_SLICE,
118 &expected,
119 concat!("Discriminator mismatch for ", $name),
120 );
121 }};
122 }
123
124 check!(Obligation, "Obligation");
125 check!(Reserve, "Reserve");
126 check!(LendingMarket, "LendingMarket");
127 check!(GlobalConfig, "GlobalConfig");
128 check!(ReferrerTokenState, "ReferrerTokenState");
129 check!(UserMetadata, "UserMetadata");
130 check!(ReferrerState, "ReferrerState");
131 check!(WithdrawTicket, "WithdrawTicket");
132 }
133}