anchor_lang/accounts/
account_loader.rs1use {
4 crate::{
5 bpf_writer::BpfWriter,
6 error::{Error, ErrorCode},
7 solana_program::{account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey},
8 Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfos, ToAccountMetas,
9 ZeroCopy,
10 },
11 std::{
12 cell::{Ref, RefMut},
13 collections::BTreeSet,
14 fmt,
15 io::Write,
16 marker::PhantomData,
17 mem,
18 ops::DerefMut,
19 },
20};
21
22#[derive(Clone)]
98pub struct AccountLoader<'info, T: ZeroCopy + Owner> {
99 acc_info: &'info AccountInfo<'info>,
100 phantom: PhantomData<&'info T>,
101}
102
103impl<T: ZeroCopy + Owner + fmt::Debug> fmt::Debug for AccountLoader<'_, T> {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 f.debug_struct("AccountLoader")
106 .field("acc_info", &self.acc_info)
107 .field("phantom", &self.phantom)
108 .finish()
109 }
110}
111
112impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
113 pub fn new_unchecked(acc_info: &'info AccountInfo<'info>) -> AccountLoader<'info, T> {
118 Self {
119 acc_info,
120 phantom: PhantomData,
121 }
122 }
123
124 #[inline(never)]
126 pub fn try_from(acc_info: &'info AccountInfo<'info>) -> Result<AccountLoader<'info, T>> {
127 if acc_info.owner != &T::owner() {
128 return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
129 .with_pubkeys((*acc_info.owner, T::owner())));
130 }
131
132 let data = &acc_info.try_borrow_data()?;
133 let disc = T::DISCRIMINATOR;
134 if data.len() < disc.len() {
135 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
136 }
137
138 let given_disc = &data[..disc.len()];
139 if given_disc != disc {
140 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
141 }
142
143 Ok(AccountLoader::new_unchecked(acc_info))
144 }
145
146 #[inline(never)]
148 pub fn try_from_unchecked(
149 _program_id: &Pubkey,
150 acc_info: &'info AccountInfo<'info>,
151 ) -> Result<AccountLoader<'info, T>> {
152 if acc_info.owner != &T::owner() {
153 return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
154 .with_pubkeys((*acc_info.owner, T::owner())));
155 }
156 Ok(AccountLoader::new_unchecked(acc_info))
157 }
158
159 fn check_size(&self, data: &[u8]) -> Result<()> {
160 let required = T::DISCRIMINATOR
161 .len()
162 .checked_add(mem::size_of::<T>())
163 .ok_or(ErrorCode::AccountDidNotDeserialize)?;
164 if data.len() < required {
165 return Err(ErrorCode::AccountDidNotDeserialize.into());
166 }
167 Ok(())
168 }
169
170 pub fn load(&self) -> Result<Ref<'_, T>> {
172 let data = self.acc_info.try_borrow_data()?;
173 let disc = T::DISCRIMINATOR;
174 if data.len() < disc.len() {
175 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
176 }
177
178 let given_disc = &data[..disc.len()];
179 if given_disc != disc {
180 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
181 }
182
183 self.check_size(&data)?;
184
185 Ok(Ref::map(data, |data| {
186 bytemuck::from_bytes(&data[disc.len()..mem::size_of::<T>() + disc.len()])
187 }))
188 }
189
190 pub fn load_mut(&self) -> Result<RefMut<'_, T>> {
192 if !self.acc_info.is_writable {
195 return Err(ErrorCode::AccountNotMutable.into());
196 }
197
198 let data = self.acc_info.try_borrow_mut_data()?;
199 let disc = T::DISCRIMINATOR;
200 if data.len() < disc.len() {
201 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
202 }
203
204 let given_disc = &data[..disc.len()];
205 if given_disc != disc {
206 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
207 }
208
209 self.check_size(&data)?;
210
211 Ok(RefMut::map(data, |data| {
212 bytemuck::from_bytes_mut(
213 &mut data.deref_mut()[disc.len()..mem::size_of::<T>() + disc.len()],
214 )
215 }))
216 }
217
218 pub fn load_init(&self) -> Result<RefMut<'_, T>> {
221 if !self.acc_info.is_writable {
224 return Err(ErrorCode::AccountNotMutable.into());
225 }
226
227 let data = self.acc_info.try_borrow_mut_data()?;
228
229 let disc = T::DISCRIMINATOR;
231 self.check_size(&data)?;
232
233 let given_disc = &data[..disc.len()];
234 let has_disc = given_disc.iter().any(|b| *b != 0);
235 if has_disc {
236 return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
237 }
238
239 Ok(RefMut::map(data, |data| {
240 bytemuck::from_bytes_mut(
241 &mut data.deref_mut()[disc.len()..mem::size_of::<T>() + disc.len()],
242 )
243 }))
244 }
245}
246
247impl<'info, B, T: ZeroCopy + Owner> Accounts<'info, B> for AccountLoader<'info, T> {
248 #[inline(never)]
249 fn try_accounts(
250 _program_id: &Pubkey,
251 accounts: &mut &'info [AccountInfo<'info>],
252 _ix_data: &[u8],
253 _bumps: &mut B,
254 _reallocs: &mut BTreeSet<Pubkey>,
255 ) -> Result<Self> {
256 if accounts.is_empty() {
257 return Err(ErrorCode::AccountNotEnoughKeys.into());
258 }
259 let account = &accounts[0];
260 *accounts = &accounts[1..];
261 let l = AccountLoader::try_from(account)?;
262 Ok(l)
263 }
264}
265
266impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T> {
267 fn exit(&self, program_id: &Pubkey) -> Result<()> {
269 if &T::owner() == program_id && !crate::common::is_closed(self.acc_info) {
271 let required = T::DISCRIMINATOR.len() + mem::size_of::<T>();
273 if self.acc_info.try_data_len()? < required {
274 return Err(ErrorCode::AccountDidNotDeserialize.into());
275 }
276 let mut data = self.acc_info.try_borrow_mut_data()?;
277 let dst: &mut [u8] = &mut data;
278 let mut writer = BpfWriter::new(dst);
279 writer.write_all(T::DISCRIMINATOR).unwrap();
280 }
281 Ok(())
282 }
283}
284
285impl<'info, T: ZeroCopy + Owner> AccountsClose<'info> for AccountLoader<'info, T> {
286 fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
287 crate::common::close(self.as_ref(), sol_destination.as_ref())
288 }
289}
290
291impl<T: ZeroCopy + Owner> ToAccountMetas for AccountLoader<'_, T> {
292 fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
293 let is_signer = is_signer.unwrap_or(self.acc_info.is_signer);
294 let meta = match self.acc_info.is_writable {
295 false => AccountMeta::new_readonly(*self.acc_info.key, is_signer),
296 true => AccountMeta::new(*self.acc_info.key, is_signer),
297 };
298 vec![meta]
299 }
300}
301
302impl<'info, T: ZeroCopy + Owner> AsRef<AccountInfo<'info>> for AccountLoader<'info, T> {
303 fn as_ref(&self) -> &AccountInfo<'info> {
304 self.acc_info
305 }
306}
307
308impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, T> {
309 fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
310 vec![self.acc_info.clone()]
311 }
312}
313
314impl<T: ZeroCopy + Owner> Key for AccountLoader<'_, T> {
315 fn key(&self) -> Pubkey {
316 *self.acc_info.key
317 }
318}