1use crate::account::AccountView;
8use crate::address::Address;
9use crate::error::ProgramError;
10use core::marker::PhantomData;
11use core::mem::MaybeUninit;
12
13#[repr(C)]
17#[derive(Debug, Clone, Copy)]
18pub struct InstructionAccount<'a> {
19 pub address: &'a Address,
21 pub is_writable: bool,
23 pub is_signer: bool,
25}
26
27impl<'a> InstructionAccount<'a> {
28 #[inline(always)]
30 pub const fn new(address: &'a Address, is_writable: bool, is_signer: bool) -> Self {
31 Self {
32 address,
33 is_writable,
34 is_signer,
35 }
36 }
37
38 #[inline(always)]
40 pub const fn readonly(address: &'a Address) -> Self {
41 Self {
42 address,
43 is_writable: false,
44 is_signer: false,
45 }
46 }
47
48 #[inline(always)]
50 pub const fn writable(address: &'a Address) -> Self {
51 Self {
52 address,
53 is_writable: true,
54 is_signer: false,
55 }
56 }
57
58 #[inline(always)]
60 pub const fn readonly_signer(address: &'a Address) -> Self {
61 Self {
62 address,
63 is_writable: false,
64 is_signer: true,
65 }
66 }
67
68 #[inline(always)]
70 pub const fn writable_signer(address: &'a Address) -> Self {
71 Self {
72 address,
73 is_writable: true,
74 is_signer: true,
75 }
76 }
77}
78
79impl<'a> From<&'a AccountView<'a>> for InstructionAccount<'a> {
80 #[inline(always)]
81 fn from(view: &'a AccountView<'a>) -> Self {
82 Self {
83 address: view.address(),
84 is_writable: view.is_writable(),
85 is_signer: view.is_signer(),
86 }
87 }
88}
89
90#[repr(C)]
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct StoredAccountMeta {
98 pub pubkey: Address,
100 pub flags: u8,
102}
103
104impl StoredAccountMeta {
105 pub const SIGNER: u8 = 0b0000_0001;
107 pub const WRITABLE: u8 = 0b0000_0010;
109
110 #[inline(always)]
112 pub const fn new(pubkey: Address, is_signer: bool, is_writable: bool) -> Self {
113 let mut flags = 0u8;
114 if is_signer {
115 flags |= Self::SIGNER;
116 }
117 if is_writable {
118 flags |= Self::WRITABLE;
119 }
120 Self { pubkey, flags }
121 }
122
123 #[inline(always)]
125 pub const fn readonly(pubkey: Address) -> Self {
126 Self::new(pubkey, false, false)
127 }
128
129 #[inline(always)]
131 pub const fn writable(pubkey: Address) -> Self {
132 Self::new(pubkey, false, true)
133 }
134
135 #[inline(always)]
137 pub const fn readonly_signer(pubkey: Address) -> Self {
138 Self::new(pubkey, true, false)
139 }
140
141 #[inline(always)]
143 pub const fn writable_signer(pubkey: Address) -> Self {
144 Self::new(pubkey, true, true)
145 }
146
147 #[inline(always)]
149 pub const fn is_signer(&self) -> bool {
150 self.flags & Self::SIGNER != 0
151 }
152
153 #[inline(always)]
155 pub const fn is_writable(&self) -> bool {
156 self.flags & Self::WRITABLE != 0
157 }
158
159 #[inline(always)]
161 pub fn to_instruction_account(&self) -> InstructionAccount<'_> {
162 InstructionAccount::new(&self.pubkey, self.is_writable(), self.is_signer())
163 }
164}
165
166#[derive(Debug, Clone, Copy)]
168pub struct StoredInstruction<'a> {
169 pub program_id: Address,
171 pub account_metas: &'a [StoredAccountMeta],
173 pub instruction_data: &'a [u8],
175}
176
177impl<'a> StoredInstruction<'a> {
178 #[inline]
180 pub fn new(
181 program_id: Address,
182 account_metas: &'a [StoredAccountMeta],
183 instruction_data: &'a [u8],
184 ) -> Result<Self, ProgramError> {
185 if account_metas.len() > crate::cpi::MAX_CPI_ACCOUNTS {
186 return Err(ProgramError::InvalidArgument);
187 }
188 Ok(Self {
189 program_id,
190 account_metas,
191 instruction_data,
192 })
193 }
194
195 #[inline(always)]
197 pub const fn account_count(&self) -> usize {
198 self.account_metas.len()
199 }
200
201 #[inline]
203 pub fn write_instruction_accounts<const N: usize>(
204 &'a self,
205 out: &'a mut [MaybeUninit<InstructionAccount<'a>>; N],
206 ) -> Result<&'a [InstructionAccount<'a>], ProgramError> {
207 if self.account_metas.len() > N {
208 return Err(ProgramError::InvalidArgument);
209 }
210 let mut index = 0;
211 while index < self.account_metas.len() {
212 out[index].write(self.account_metas[index].to_instruction_account());
213 index += 1;
214 }
215 Ok(unsafe {
218 core::slice::from_raw_parts(
219 out.as_ptr() as *const InstructionAccount<'a>,
220 self.account_metas.len(),
221 )
222 })
223 }
224
225 #[inline]
227 pub fn to_instruction_view<const N: usize>(
228 &'a self,
229 out: &'a mut [MaybeUninit<InstructionAccount<'a>>; N],
230 ) -> Result<InstructionView<'a, 'a, 'a, 'a>, ProgramError> {
231 let accounts = self.write_instruction_accounts(out)?;
232 Ok(InstructionView {
233 program_id: &self.program_id,
234 data: self.instruction_data,
235 accounts,
236 })
237 }
238}
239
240#[derive(Debug, Clone)]
244pub struct InstructionView<'a, 'b, 'c, 'd>
245where
246 'a: 'b,
247{
248 pub program_id: &'c Address,
250 pub data: &'d [u8],
252 pub accounts: &'b [InstructionAccount<'a>],
254}
255
256#[repr(C)]
263#[derive(Clone, Copy, Debug)]
264pub struct CpiAccount<'a> {
265 address: *const Address,
266 lamports: *const u64,
267 data_len: u64,
268 data: *const u8,
269 owner: *const Address,
270 rent_epoch: u64,
271 is_signer: bool,
272 is_writable: bool,
273 executable: bool,
274 _account_view: PhantomData<&'a AccountView<'a>>,
275}
276
277impl<'a> From<&'a AccountView<'a>> for CpiAccount<'a> {
278 #[inline]
279 fn from(view: &'a AccountView<'a>) -> Self {
280 let raw = view.account_ptr();
281 Self {
285 address: unsafe { core::ptr::addr_of!((*raw).address) as *const Address },
287 lamports: unsafe { core::ptr::addr_of!((*raw).lamports) },
288 data_len: view.data_len() as u64,
289 data: view.data_ptr_unchecked(),
290 owner: unsafe { core::ptr::addr_of!((*raw).owner) as *const Address },
292 rent_epoch: 0,
293 is_signer: view.is_signer(),
294 is_writable: view.is_writable(),
295 executable: view.executable(),
296 _account_view: PhantomData,
297 }
298 }
299}
300
301#[repr(C)]
305#[derive(Debug, Clone)]
306pub struct Seed<'a> {
307 pub(crate) seed: *const u8,
308 pub(crate) len: u64,
309 _bytes: PhantomData<&'a [u8]>,
310}
311
312impl<'a> From<&'a [u8]> for Seed<'a> {
313 #[inline(always)]
314 fn from(bytes: &'a [u8]) -> Self {
315 Self {
316 seed: bytes.as_ptr(),
317 len: bytes.len() as u64,
318 _bytes: PhantomData,
319 }
320 }
321}
322
323impl<'a, const N: usize> From<&'a [u8; N]> for Seed<'a> {
324 #[inline(always)]
325 fn from(bytes: &'a [u8; N]) -> Self {
326 Self {
327 seed: bytes.as_ptr(),
328 len: N as u64,
329 _bytes: PhantomData,
330 }
331 }
332}
333
334impl core::ops::Deref for Seed<'_> {
335 type Target = [u8];
336
337 #[inline(always)]
338 fn deref(&self) -> &[u8] {
339 unsafe { core::slice::from_raw_parts(self.seed, self.len as usize) }
341 }
342}
343
344#[repr(C)]
348#[derive(Debug, Clone)]
349pub struct Signer<'a, 'b> {
350 pub(crate) seeds: *const Seed<'a>,
351 pub(crate) len: u64,
352 _seeds: PhantomData<&'b [Seed<'a>]>,
353}
354
355impl<'a, 'b> From<&'b [Seed<'a>]> for Signer<'a, 'b> {
356 #[inline(always)]
357 fn from(seeds: &'b [Seed<'a>]) -> Self {
358 Self {
359 seeds: seeds.as_ptr(),
360 len: seeds.len() as u64,
361 _seeds: PhantomData,
362 }
363 }
364}
365
366impl<'a, 'b, const N: usize> From<&'b [Seed<'a>; N]> for Signer<'a, 'b> {
367 #[inline(always)]
368 fn from(seeds: &'b [Seed<'a>; N]) -> Self {
369 Self {
370 seeds: seeds.as_ptr(),
371 len: N as u64,
372 _seeds: PhantomData,
373 }
374 }
375}
376
377#[macro_export]
381macro_rules! seeds {
382 ( $($seed:expr),* $(,)? ) => {
383 [$(
384 $crate::instruction::Seed::from($seed),
385 )*]
386 };
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn stored_account_meta_flags_round_trip() {
395 let key = Address::new_from_array([3; 32]);
396 let meta = StoredAccountMeta::writable_signer(key);
397 assert!(meta.is_signer());
398 assert!(meta.is_writable());
399
400 let ix_meta = meta.to_instruction_account();
401 assert_eq!(ix_meta.address, &key);
402 assert!(ix_meta.is_signer);
403 assert!(ix_meta.is_writable);
404 }
405
406 #[test]
407 fn stored_instruction_builds_instruction_view_without_alloc() {
408 let program = Address::new_from_array([9; 32]);
409 let first = Address::new_from_array([1; 32]);
410 let second = Address::new_from_array([2; 32]);
411 let metas = [
412 StoredAccountMeta::readonly(first),
413 StoredAccountMeta::writable(second),
414 ];
415 let data = [7u8, 8, 9];
416 let stored = StoredInstruction::new(program, &metas, &data).unwrap();
417 let mut out: [MaybeUninit<InstructionAccount<'_>>; 2] =
418 [MaybeUninit::uninit(), MaybeUninit::uninit()];
419
420 let view = stored.to_instruction_view(&mut out).unwrap();
421 assert_eq!(view.program_id, &program);
422 assert_eq!(view.data, &data);
423 assert_eq!(view.accounts.len(), 2);
424 assert_eq!(view.accounts[0].address, &first);
425 assert!(!view.accounts[0].is_writable);
426 assert_eq!(view.accounts[1].address, &second);
427 assert!(view.accounts[1].is_writable);
428 }
429
430 #[test]
431 fn stored_instruction_rejects_small_output_buffer() {
432 let program = Address::new_from_array([9; 32]);
433 let first = Address::new_from_array([1; 32]);
434 let second = Address::new_from_array([2; 32]);
435 let metas = [
436 StoredAccountMeta::readonly(first),
437 StoredAccountMeta::writable(second),
438 ];
439 let stored = StoredInstruction::new(program, &metas, &[]).unwrap();
440 let mut out: [MaybeUninit<InstructionAccount<'_>>; 1] = [MaybeUninit::uninit()];
441
442 assert_eq!(
443 stored.to_instruction_view(&mut out).unwrap_err(),
444 ProgramError::InvalidArgument
445 );
446 }
447}