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
277const _: () = {
281 use core::mem::{offset_of, size_of};
282 let signer = offset_of!(CpiAccount<'static>, is_signer);
283 assert!(offset_of!(CpiAccount<'static>, is_writable) == signer + 1);
284 assert!(offset_of!(CpiAccount<'static>, executable) == signer + 2);
285 assert!(signer + 4 <= size_of::<CpiAccount<'static>>());
286};
287
288impl<'a> From<&'a AccountView<'a>> for CpiAccount<'a> {
289 #[inline(always)]
290 fn from(view: &'a AccountView<'a>) -> Self {
291 let raw = view.account_ptr();
292 let mut out = core::mem::MaybeUninit::<Self>::uninit();
293 let slot = out.as_mut_ptr();
294 unsafe {
308 core::ptr::addr_of_mut!((*slot).address)
309 .write(core::ptr::addr_of!((*raw).address) as *const Address);
310 core::ptr::addr_of_mut!((*slot).lamports).write(core::ptr::addr_of!((*raw).lamports));
311 core::ptr::addr_of_mut!((*slot).data_len).write(view.data_len() as u64);
312 core::ptr::addr_of_mut!((*slot).data).write(view.data_ptr_unchecked());
313 core::ptr::addr_of_mut!((*slot).owner)
314 .write(core::ptr::addr_of!((*raw).owner) as *const Address);
315 core::ptr::addr_of_mut!((*slot).rent_epoch).write(0);
316 let flags = core::ptr::read_unaligned((raw as *const u8).add(1) as *const u32);
317 (core::ptr::addr_of_mut!((*slot).is_signer) as *mut u32).write_unaligned(flags);
318 out.assume_init()
319 }
320 }
321}
322
323#[repr(C)]
327#[derive(Debug, Clone)]
328pub struct Seed<'a> {
329 pub(crate) seed: *const u8,
330 pub(crate) len: u64,
331 _bytes: PhantomData<&'a [u8]>,
332}
333
334impl<'a> From<&'a [u8]> for Seed<'a> {
335 #[inline(always)]
336 fn from(bytes: &'a [u8]) -> Self {
337 Self {
338 seed: bytes.as_ptr(),
339 len: bytes.len() as u64,
340 _bytes: PhantomData,
341 }
342 }
343}
344
345impl<'a, const N: usize> From<&'a [u8; N]> for Seed<'a> {
346 #[inline(always)]
347 fn from(bytes: &'a [u8; N]) -> Self {
348 Self {
349 seed: bytes.as_ptr(),
350 len: N as u64,
351 _bytes: PhantomData,
352 }
353 }
354}
355
356impl core::ops::Deref for Seed<'_> {
357 type Target = [u8];
358
359 #[inline(always)]
360 fn deref(&self) -> &[u8] {
361 unsafe { core::slice::from_raw_parts(self.seed, self.len as usize) }
364 }
365}
366
367#[repr(C)]
371#[derive(Debug, Clone)]
372pub struct Signer<'a, 'b> {
373 pub(crate) seeds: *const Seed<'a>,
374 pub(crate) len: u64,
375 _seeds: PhantomData<&'b [Seed<'a>]>,
376}
377
378impl<'a, 'b> From<&'b [Seed<'a>]> for Signer<'a, 'b> {
379 #[inline(always)]
380 fn from(seeds: &'b [Seed<'a>]) -> Self {
381 Self {
382 seeds: seeds.as_ptr(),
383 len: seeds.len() as u64,
384 _seeds: PhantomData,
385 }
386 }
387}
388
389impl<'a, 'b, const N: usize> From<&'b [Seed<'a>; N]> for Signer<'a, 'b> {
390 #[inline(always)]
391 fn from(seeds: &'b [Seed<'a>; N]) -> Self {
392 Self {
393 seeds: seeds.as_ptr(),
394 len: N as u64,
395 _seeds: PhantomData,
396 }
397 }
398}
399
400#[macro_export]
404macro_rules! seeds {
405 ( $($seed:expr),* $(,)? ) => {
406 [$(
407 $crate::instruction::Seed::from($seed),
408 )*]
409 };
410}
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415
416 #[test]
417 fn stored_account_meta_flags_round_trip() {
418 let key = Address::new_from_array([3; 32]);
419 let meta = StoredAccountMeta::writable_signer(key);
420 assert!(meta.is_signer());
421 assert!(meta.is_writable());
422
423 let ix_meta = meta.to_instruction_account();
424 assert_eq!(ix_meta.address, &key);
425 assert!(ix_meta.is_signer);
426 assert!(ix_meta.is_writable);
427 }
428
429 #[test]
430 fn stored_instruction_builds_instruction_view_without_alloc() {
431 let program = Address::new_from_array([9; 32]);
432 let first = Address::new_from_array([1; 32]);
433 let second = Address::new_from_array([2; 32]);
434 let metas = [
435 StoredAccountMeta::readonly(first),
436 StoredAccountMeta::writable(second),
437 ];
438 let data = [7u8, 8, 9];
439 let stored = StoredInstruction::new(program, &metas, &data).unwrap();
440 let mut out: [MaybeUninit<InstructionAccount<'_>>; 2] =
441 [MaybeUninit::uninit(), MaybeUninit::uninit()];
442
443 let view = stored.to_instruction_view(&mut out).unwrap();
444 assert_eq!(view.program_id, &program);
445 assert_eq!(view.data, &data);
446 assert_eq!(view.accounts.len(), 2);
447 assert_eq!(view.accounts[0].address, &first);
448 assert!(!view.accounts[0].is_writable);
449 assert_eq!(view.accounts[1].address, &second);
450 assert!(view.accounts[1].is_writable);
451 }
452
453 #[test]
454 fn stored_instruction_rejects_small_output_buffer() {
455 let program = Address::new_from_array([9; 32]);
456 let first = Address::new_from_array([1; 32]);
457 let second = Address::new_from_array([2; 32]);
458 let metas = [
459 StoredAccountMeta::readonly(first),
460 StoredAccountMeta::writable(second),
461 ];
462 let stored = StoredInstruction::new(program, &metas, &[]).unwrap();
463 let mut out: [MaybeUninit<InstructionAccount<'_>>; 1] = [MaybeUninit::uninit()];
464
465 assert_eq!(
466 stored.to_instruction_view(&mut out).unwrap_err(),
467 ProgramError::InvalidArgument
468 );
469 }
470}