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 fn new(acc_info: &'info AccountInfo<'info>) -> AccountLoader<'info, T> {
114 Self {
115 acc_info,
116 phantom: PhantomData,
117 }
118 }
119
120 #[inline(never)]
122 pub fn try_from(acc_info: &'info AccountInfo<'info>) -> Result<AccountLoader<'info, T>> {
123 if acc_info.owner != &T::owner() {
124 return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
125 .with_pubkeys((*acc_info.owner, T::owner())));
126 }
127
128 let data = &acc_info.try_borrow_data()?;
129 let disc = T::DISCRIMINATOR;
130 if data.len() < disc.len() {
131 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
132 }
133
134 let given_disc = &data[..disc.len()];
135 if given_disc != disc {
136 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
137 }
138
139 Ok(AccountLoader::new(acc_info))
140 }
141
142 #[inline(never)]
144 pub fn try_from_unchecked(
145 _program_id: &Pubkey,
146 acc_info: &'info AccountInfo<'info>,
147 ) -> Result<AccountLoader<'info, T>> {
148 if acc_info.owner != &T::owner() {
149 return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
150 .with_pubkeys((*acc_info.owner, T::owner())));
151 }
152 Ok(AccountLoader::new(acc_info))
153 }
154
155 pub fn load(&self) -> Result<Ref<'_, T>> {
157 let data = self.acc_info.try_borrow_data()?;
158 let disc = T::DISCRIMINATOR;
159 if data.len() < disc.len() {
160 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
161 }
162
163 let given_disc = &data[..disc.len()];
164 if given_disc != disc {
165 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
166 }
167
168 Ok(Ref::map(data, |data| {
169 bytemuck::from_bytes(&data[disc.len()..mem::size_of::<T>() + disc.len()])
170 }))
171 }
172
173 pub fn load_mut(&self) -> Result<RefMut<'_, T>> {
175 if !self.acc_info.is_writable {
178 return Err(ErrorCode::AccountNotMutable.into());
179 }
180
181 let data = self.acc_info.try_borrow_mut_data()?;
182 let disc = T::DISCRIMINATOR;
183 if data.len() < disc.len() {
184 return Err(ErrorCode::AccountDiscriminatorNotFound.into());
185 }
186
187 let given_disc = &data[..disc.len()];
188 if given_disc != disc {
189 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
190 }
191
192 Ok(RefMut::map(data, |data| {
193 bytemuck::from_bytes_mut(
194 &mut data.deref_mut()[disc.len()..mem::size_of::<T>() + disc.len()],
195 )
196 }))
197 }
198
199 pub fn load_init(&self) -> Result<RefMut<'_, T>> {
202 if !self.acc_info.is_writable {
205 return Err(ErrorCode::AccountNotMutable.into());
206 }
207
208 let data = self.acc_info.try_borrow_mut_data()?;
209
210 let disc = T::DISCRIMINATOR;
212 let given_disc = &data[..disc.len()];
213 let has_disc = given_disc.iter().any(|b| *b != 0);
214 if has_disc {
215 return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
216 }
217
218 Ok(RefMut::map(data, |data| {
219 bytemuck::from_bytes_mut(
220 &mut data.deref_mut()[disc.len()..mem::size_of::<T>() + disc.len()],
221 )
222 }))
223 }
224}
225
226impl<'info, B, T: ZeroCopy + Owner> Accounts<'info, B> for AccountLoader<'info, T> {
227 #[inline(never)]
228 fn try_accounts(
229 _program_id: &Pubkey,
230 accounts: &mut &'info [AccountInfo<'info>],
231 _ix_data: &[u8],
232 _bumps: &mut B,
233 _reallocs: &mut BTreeSet<Pubkey>,
234 ) -> Result<Self> {
235 if accounts.is_empty() {
236 return Err(ErrorCode::AccountNotEnoughKeys.into());
237 }
238 let account = &accounts[0];
239 *accounts = &accounts[1..];
240 let l = AccountLoader::try_from(account)?;
241 Ok(l)
242 }
243}
244
245impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T> {
246 fn exit(&self, program_id: &Pubkey) -> Result<()> {
248 if &T::owner() == program_id && !crate::common::is_closed(self.acc_info) {
250 let mut data = self.acc_info.try_borrow_mut_data()?;
251 let dst: &mut [u8] = &mut data;
252 let mut writer = BpfWriter::new(dst);
253 writer.write_all(T::DISCRIMINATOR).unwrap();
254 }
255 Ok(())
256 }
257}
258
259impl<'info, T: ZeroCopy + Owner> AccountsClose<'info> for AccountLoader<'info, T> {
260 fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
261 crate::common::close(self.as_ref(), sol_destination.as_ref())
262 }
263}
264
265impl<T: ZeroCopy + Owner> ToAccountMetas for AccountLoader<'_, T> {
266 fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
267 let is_signer = is_signer.unwrap_or(self.acc_info.is_signer);
268 let meta = match self.acc_info.is_writable {
269 false => AccountMeta::new_readonly(*self.acc_info.key, is_signer),
270 true => AccountMeta::new(*self.acc_info.key, is_signer),
271 };
272 vec![meta]
273 }
274}
275
276impl<'info, T: ZeroCopy + Owner> AsRef<AccountInfo<'info>> for AccountLoader<'info, T> {
277 fn as_ref(&self) -> &AccountInfo<'info> {
278 self.acc_info
279 }
280}
281
282impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, T> {
283 fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
284 vec![self.acc_info.clone()]
285 }
286}
287
288impl<T: ZeroCopy + Owner> Key for AccountLoader<'_, T> {
289 fn key(&self) -> Pubkey {
290 *self.acc_info.key
291 }
292}