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