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