1use crate::{
40 account::AccountView,
41 account_wrappers::{Signer, UncheckedAccount},
42 error::ProgramError,
43 foreign::{ExternalAccount, ExternalZeroCopy},
44 ProgramResult,
45};
46
47pub const MAX_REMAINING_ACCOUNTS: usize = 64;
52
53#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55pub enum RemainingError {
56 DuplicateAccount,
60 Overflow,
63}
64
65impl From<RemainingError> for ProgramError {
66 fn from(e: RemainingError) -> Self {
67 match e {
68 RemainingError::DuplicateAccount => ProgramError::InvalidAccountData,
69 RemainingError::Overflow => ProgramError::InvalidArgument,
70 }
71 }
72}
73
74#[derive(Copy, Clone, Eq, PartialEq, Debug)]
76pub enum RemainingMode {
77 Strict,
81 Passthrough,
84}
85
86pub struct RemainingAccounts<'a> {
94 declared: &'a [AccountView<'a>],
96 remaining: &'a [AccountView<'a>],
98 mode: RemainingMode,
100}
101
102impl<'a> RemainingAccounts<'a> {
103 #[inline(always)]
105 pub fn strict(declared: &'a [AccountView<'a>], remaining: &'a [AccountView<'a>]) -> Self {
106 Self {
107 declared,
108 remaining,
109 mode: RemainingMode::Strict,
110 }
111 }
112
113 #[inline(always)]
115 pub fn passthrough(declared: &'a [AccountView<'a>], remaining: &'a [AccountView<'a>]) -> Self {
116 Self {
117 declared,
118 remaining,
119 mode: RemainingMode::Passthrough,
120 }
121 }
122
123 #[inline(always)]
125 pub fn len(&self) -> usize {
126 self.remaining.len()
127 }
128
129 #[inline(always)]
131 pub fn is_empty(&self) -> bool {
132 self.remaining.is_empty()
133 }
134
135 #[inline(always)]
137 pub fn mode(&self) -> RemainingMode {
138 self.mode
139 }
140
141 #[inline(always)]
143 pub fn as_slice(&self) -> &'a [AccountView<'a>] {
144 self.remaining
145 }
146
147 pub fn get(&self, index: usize) -> Result<Option<&'a AccountView<'a>>, ProgramError> {
151 if index >= self.remaining.len() {
152 return Ok(None);
153 }
154 let candidate = &self.remaining[index];
155 match self.mode {
156 RemainingMode::Passthrough => Ok(Some(candidate)),
157 RemainingMode::Strict => {
158 if index >= MAX_REMAINING_ACCOUNTS {
159 return Err(RemainingError::Overflow.into());
160 }
161 for d in self.declared {
163 if d.address() == candidate.address() {
164 return Err(RemainingError::DuplicateAccount.into());
165 }
166 }
167 for r in &self.remaining[..index] {
169 if r.address() == candidate.address() {
170 return Err(RemainingError::DuplicateAccount.into());
171 }
172 }
173 Ok(Some(candidate))
174 }
175 }
176 }
177
178 pub fn account_views<const N: usize>(
183 &self,
184 ) -> Result<RemainingAccountViews<'a, N>, ProgramError> {
185 if self.remaining.len() > N {
186 return Err(RemainingError::Overflow.into());
187 }
188 let mut items: [Option<&'a AccountView<'a>>; N] = [None; N];
189 let mut index = 0;
190 while index < self.remaining.len() {
191 let account = self.get(index)?.ok_or(ProgramError::NotEnoughAccountKeys)?;
192 items[index] = Some(account);
193 index += 1;
194 }
195 Ok(RemainingAccountViews { items, len: index })
196 }
197
198 pub fn signers<const N: usize>(&self) -> Result<RemainingSigners<'a, N>, ProgramError> {
203 if self.remaining.len() > N {
204 return Err(RemainingError::Overflow.into());
205 }
206 let mut items: [Option<Signer<'a>>; N] = [None; N];
207 let mut index = 0;
208 while index < self.remaining.len() {
209 let account = self.get(index)?.ok_or(ProgramError::NotEnoughAccountKeys)?;
210 items[index] = Some(Signer::try_new(account)?);
211 index += 1;
212 }
213 Ok(RemainingSigners { items, len: index })
214 }
215
216 #[inline(always)]
220 pub fn iter(&self) -> RemainingIter<'a> {
221 RemainingIter {
222 declared: self.declared,
223 remaining: self.remaining,
224 mode: self.mode,
225 index: 0,
226 }
227 }
228
229 #[inline(always)]
235 pub fn typed(&self) -> RemainingTyped<'a> {
236 RemainingTyped {
237 declared: self.declared,
238 remaining: self.remaining,
239 mode: self.mode,
240 index: 0,
241 }
242 }
243
244 #[inline(always)]
250 pub fn lazy(&self) -> RemainingLazy<'a> {
251 RemainingLazy {
252 declared: self.declared,
253 remaining: self.remaining,
254 mode: self.mode,
255 }
256 }
257}
258
259pub struct RemainingIter<'a> {
261 declared: &'a [AccountView<'a>],
262 remaining: &'a [AccountView<'a>],
263 mode: RemainingMode,
264 index: usize,
265}
266
267impl<'a> Iterator for RemainingIter<'a> {
268 type Item = Result<&'a AccountView<'a>, ProgramError>;
269
270 fn next(&mut self) -> Option<Self::Item> {
271 if self.index >= self.remaining.len() {
272 return None;
273 }
274 if self.index >= MAX_REMAINING_ACCOUNTS {
275 self.index = self.remaining.len();
278 return Some(Err(RemainingError::Overflow.into()));
279 }
280 let candidate = &self.remaining[self.index];
281 let i = self.index;
282 self.index = self.index.wrapping_add(1);
283
284 if matches!(self.mode, RemainingMode::Strict) {
285 for d in self.declared {
286 if d.address() == candidate.address() {
287 return Some(Err(RemainingError::DuplicateAccount.into()));
288 }
289 }
290 for r in &self.remaining[..i] {
291 if r.address() == candidate.address() {
292 return Some(Err(RemainingError::DuplicateAccount.into()));
293 }
294 }
295 }
296 Some(Ok(candidate))
297 }
298}
299
300pub struct RemainingAccountViews<'a, const N: usize> {
302 items: [Option<&'a AccountView<'a>>; N],
303 len: usize,
304}
305
306impl<'a, const N: usize> RemainingAccountViews<'a, N> {
307 #[inline(always)]
309 pub const fn len(&self) -> usize {
310 self.len
311 }
312
313 #[inline(always)]
315 pub const fn is_empty(&self) -> bool {
316 self.len == 0
317 }
318
319 #[inline(always)]
321 pub fn get(&self, index: usize) -> Option<&'a AccountView<'a>> {
322 if index >= self.len {
323 None
324 } else {
325 self.items[index]
326 }
327 }
328
329 #[inline(always)]
331 pub fn iter(&self) -> RemainingAccountViewIter<'_, 'a, N> {
332 RemainingAccountViewIter {
333 set: self,
334 index: 0,
335 }
336 }
337}
338
339pub struct RemainingAccountViewIter<'set, 'a, const N: usize> {
341 set: &'set RemainingAccountViews<'a, N>,
342 index: usize,
343}
344
345impl<'a, const N: usize> Iterator for RemainingAccountViewIter<'_, 'a, N> {
346 type Item = &'a AccountView<'a>;
347
348 fn next(&mut self) -> Option<Self::Item> {
349 if self.index >= self.set.len {
350 return None;
351 }
352 let item = self.set.items[self.index];
353 self.index += 1;
354 item
355 }
356}
357
358pub struct RemainingSigners<'a, const N: usize> {
360 items: [Option<Signer<'a>>; N],
361 len: usize,
362}
363
364impl<'a, const N: usize> RemainingSigners<'a, N> {
365 #[inline(always)]
367 pub const fn len(&self) -> usize {
368 self.len
369 }
370
371 #[inline(always)]
373 pub const fn is_empty(&self) -> bool {
374 self.len == 0
375 }
376
377 #[inline(always)]
379 pub fn get(&self, index: usize) -> Option<Signer<'a>> {
380 if index >= self.len {
381 None
382 } else {
383 self.items[index]
384 }
385 }
386
387 #[inline(always)]
389 pub fn iter(&self) -> RemainingSignerIter<'_, 'a, N> {
390 RemainingSignerIter {
391 set: self,
392 index: 0,
393 }
394 }
395}
396
397pub struct RemainingSignerIter<'set, 'a, const N: usize> {
399 set: &'set RemainingSigners<'a, N>,
400 index: usize,
401}
402
403impl<'a, const N: usize> Iterator for RemainingSignerIter<'_, 'a, N> {
404 type Item = Signer<'a>;
405
406 fn next(&mut self) -> Option<Self::Item> {
407 if self.index >= self.set.len {
408 return None;
409 }
410 let item = self.set.items[self.index];
411 self.index += 1;
412 item
413 }
414}
415
416pub struct RemainingTyped<'a> {
418 declared: &'a [AccountView<'a>],
419 remaining: &'a [AccountView<'a>],
420 mode: RemainingMode,
421 index: usize,
422}
423
424impl<'a> RemainingTyped<'a> {
425 #[inline(always)]
426 fn view(&self) -> RemainingAccounts<'a> {
427 RemainingAccounts {
428 declared: self.declared,
429 remaining: self.remaining,
430 mode: self.mode,
431 }
432 }
433
434 #[inline(always)]
436 pub const fn consumed(&self) -> usize {
437 self.index
438 }
439
440 #[inline(always)]
442 pub fn remaining_len(&self) -> usize {
443 self.remaining.len().saturating_sub(self.index)
444 }
445
446 #[inline(always)]
448 pub fn is_empty(&self) -> bool {
449 self.index >= self.remaining.len()
450 }
451
452 pub fn next_account(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
454 let account = self
455 .view()
456 .get(self.index)?
457 .ok_or(ProgramError::NotEnoughAccountKeys)?;
458 self.index += 1;
459 Ok(account)
460 }
461
462 #[inline]
464 pub fn next_unchecked(&mut self) -> Result<UncheckedAccount<'a>, ProgramError> {
465 Ok(UncheckedAccount::new(self.next_account()?))
466 }
467
468 #[inline]
470 pub fn next_signer(&mut self) -> Result<Signer<'a>, ProgramError> {
471 Signer::try_new(self.next_account()?)
472 }
473
474 #[inline]
476 pub fn next_external<T: ExternalZeroCopy>(
477 &mut self,
478 ) -> Result<ExternalAccount<'a, T>, ProgramError> {
479 ExternalAccount::try_new(self.next_account()?)
480 }
481
482 #[inline]
484 pub fn no_duplicates(self) -> Result<Self, ProgramError> {
485 self.assert_no_duplicates()?;
486 Ok(self)
487 }
488
489 pub fn take_group(&mut self, len: usize) -> Result<RemainingGroup<'a>, ProgramError> {
494 let end = self
495 .index
496 .checked_add(len)
497 .ok_or(ProgramError::ArithmeticOverflow)?;
498 if end > self.remaining.len() {
499 return Err(ProgramError::NotEnoughAccountKeys);
500 }
501 let group = RemainingGroup {
502 parser: RemainingTyped {
503 declared: self.declared,
504 remaining: &self.remaining[self.index..end],
505 mode: self.mode,
506 index: 0,
507 },
508 };
509 self.index = end;
510 Ok(group)
511 }
512
513 pub fn assert_no_duplicates(&self) -> ProgramResult {
515 let strict = RemainingAccounts {
516 declared: self.declared,
517 remaining: self.remaining,
518 mode: RemainingMode::Strict,
519 };
520 let mut index = 0;
521 while index < self.remaining.len() {
522 strict
523 .get(index)?
524 .ok_or(ProgramError::NotEnoughAccountKeys)?;
525 index += 1;
526 }
527 Ok(())
528 }
529
530 pub fn assert_sorted_by<K, F>(&self, mut key: F) -> ProgramResult
532 where
533 K: Ord,
534 F: FnMut(&'a AccountView<'a>) -> Result<K, ProgramError>,
535 {
536 let view = self.view();
537 let mut previous: Option<K> = None;
538 let mut index = 0;
539 while index < self.remaining.len() {
540 let account = view.get(index)?.ok_or(ProgramError::NotEnoughAccountKeys)?;
541 let current = key(account)?;
542 if let Some(ref last) = previous {
543 if current < *last {
544 return Err(ProgramError::InvalidAccountData);
545 }
546 }
547 previous = Some(current);
548 index += 1;
549 }
550 Ok(())
551 }
552
553 #[inline]
555 pub fn assert_empty(&self) -> ProgramResult {
556 if self.is_empty() {
557 Ok(())
558 } else {
559 Err(ProgramError::InvalidArgument)
560 }
561 }
562}
563
564pub struct RemainingGroup<'a> {
566 parser: RemainingTyped<'a>,
567}
568
569impl<'a> RemainingGroup<'a> {
570 #[inline(always)]
572 pub fn remaining_len(&self) -> usize {
573 self.parser.remaining_len()
574 }
575
576 #[inline]
578 pub fn next_account(&mut self) -> Result<&'a AccountView<'a>, ProgramError> {
579 self.parser.next_account()
580 }
581
582 #[inline]
584 pub fn next_signer(&mut self) -> Result<Signer<'a>, ProgramError> {
585 self.parser.next_signer()
586 }
587
588 #[inline]
590 pub fn next_external<T: ExternalZeroCopy>(
591 &mut self,
592 ) -> Result<ExternalAccount<'a, T>, ProgramError> {
593 self.parser.next_external::<T>()
594 }
595
596 pub fn parse_external<T: ExternalZeroCopy, const N: usize>(
598 &mut self,
599 ) -> Result<RemainingExternalAccounts<'a, T, N>, ProgramError> {
600 if self.parser.remaining_len() > N {
601 return Err(RemainingError::Overflow.into());
602 }
603 let mut items: [Option<ExternalAccount<'a, T>>; N] = [None; N];
604 let mut len = 0;
605 while !self.parser.is_empty() {
606 items[len] = Some(self.parser.next_external::<T>()?);
607 len += 1;
608 }
609 Ok(RemainingExternalAccounts { items, len })
610 }
611
612 #[inline]
614 pub fn assert_empty(&self) -> ProgramResult {
615 self.parser.assert_empty()
616 }
617}
618
619pub struct RemainingExternalAccounts<'a, T: ExternalZeroCopy, const N: usize> {
621 items: [Option<ExternalAccount<'a, T>>; N],
622 len: usize,
623}
624
625impl<'a, T: ExternalZeroCopy, const N: usize> RemainingExternalAccounts<'a, T, N> {
626 #[inline(always)]
628 pub const fn len(&self) -> usize {
629 self.len
630 }
631
632 #[inline(always)]
634 pub const fn is_empty(&self) -> bool {
635 self.len == 0
636 }
637
638 #[inline(always)]
640 pub fn get(&self, index: usize) -> Option<ExternalAccount<'a, T>> {
641 if index >= self.len {
642 None
643 } else {
644 self.items[index]
645 }
646 }
647}
648
649pub struct RemainingLazy<'a> {
651 declared: &'a [AccountView<'a>],
652 remaining: &'a [AccountView<'a>],
653 mode: RemainingMode,
654}
655
656impl<'a> RemainingLazy<'a> {
657 #[inline(always)]
658 fn view(&self) -> RemainingAccounts<'a> {
659 RemainingAccounts {
660 declared: self.declared,
661 remaining: self.remaining,
662 mode: self.mode,
663 }
664 }
665
666 #[inline(always)]
668 pub fn len(&self) -> usize {
669 self.remaining.len()
670 }
671
672 #[inline(always)]
674 pub fn is_empty(&self) -> bool {
675 self.remaining.is_empty()
676 }
677
678 pub fn at(&self, index: usize) -> Result<RemainingLazySlot<'a>, ProgramError> {
681 let account = self
682 .view()
683 .get(index)?
684 .ok_or(ProgramError::NotEnoughAccountKeys)?;
685 Ok(RemainingLazySlot { account })
686 }
687}
688
689pub struct RemainingLazySlot<'a> {
691 account: &'a AccountView<'a>,
692}
693
694impl<'a> RemainingLazySlot<'a> {
695 #[inline(always)]
697 pub const fn account(&self) -> &'a AccountView<'a> {
698 self.account
699 }
700
701 #[inline(always)]
703 pub fn unchecked(&self) -> UncheckedAccount<'a> {
704 UncheckedAccount::new(self.account)
705 }
706
707 #[inline]
709 pub fn signer(&self) -> Result<Signer<'a>, ProgramError> {
710 Signer::try_new(self.account)
711 }
712
713 #[inline]
715 pub fn external<T: ExternalZeroCopy>(&self) -> Result<ExternalAccount<'a, T>, ProgramError> {
716 ExternalAccount::try_new(self.account)
717 }
718}
719
720#[inline(always)]
723pub fn strict<'a>(
724 declared: &'a [AccountView<'a>],
725 remaining: &'a [AccountView<'a>],
726) -> RemainingAccounts<'a> {
727 RemainingAccounts::strict(declared, remaining)
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733 use crate::Address;
734 use hopper_native::{
735 AccountView as NativeAccountView, Address as NativeAddress, RuntimeAccount, NOT_BORROWED,
736 };
737 const EXTERNAL_OWNER: Address = Address::new_from_array([5; 32]);
738 struct SampleExternal;
739 impl ExternalZeroCopy for SampleExternal {
740 type View<'a> = crate::foreign::ExternalBytes<'a>;
741
742 const OWNER: Option<Address> = Some(EXTERNAL_OWNER);
743 const DISCRIMINATOR: Option<&'static [u8]> = Some(b"EX");
744 const MIN_LEN: usize = 4;
745
746 fn view<'a>(data: crate::Ref<'a, [u8]>) -> Result<Self::View<'a>, ProgramError> {
747 Ok(crate::foreign::ExternalBytes::new(data))
748 }
749 }
750 fn make_account(
751 address: [u8; 32],
752 owner: Address,
753 signer: bool,
754 data: &[u8],
755 ) -> (std::vec::Vec<u64>, AccountView<'static>) {
756 let mut backing = std::vec![0u64; (RuntimeAccount::SIZE + data.len()).div_ceil(8)];
757 let raw = backing.as_mut_ptr() as *mut RuntimeAccount;
758 unsafe {
761 raw.write(RuntimeAccount {
762 borrow_state: NOT_BORROWED,
763 is_signer: signer as u8,
764 is_writable: 0,
765 executable: 0,
766 resize_delta: 0,
767 address: NativeAddress::new_from_array(address),
768 owner: NativeAddress::new_from_array(owner.to_bytes()),
769 lamports: 1,
770 data_len: data.len() as u64,
771 });
772 let data_ptr = (backing.as_mut_ptr() as *mut u8).add(RuntimeAccount::SIZE);
773 core::ptr::copy_nonoverlapping(data.as_ptr(), data_ptr, data.len());
774 }
775 let backend = unsafe { NativeAccountView::new_unchecked(raw) };
777 (backing, AccountView::from_backend(backend))
778 }
779
780 #[test]
786 fn error_variants_surface_as_program_error() {
787 let dup: ProgramError = RemainingError::DuplicateAccount.into();
788 assert_eq!(dup, ProgramError::InvalidAccountData);
789 let ovf: ProgramError = RemainingError::Overflow.into();
790 assert_eq!(ovf, ProgramError::InvalidArgument);
791 }
792
793 #[test]
794 fn max_remaining_matches_quasar() {
795 assert_eq!(MAX_REMAINING_ACCOUNTS, 64);
797 }
798 #[test]
799 fn typed_remaining_parses_external_signer_and_raw_slots() {
800 let (_declared_backing, declared) =
801 make_account([1; 32], Address::new_from_array([9; 32]), false, b"");
802 let (_external_backing, external) = make_account([2; 32], EXTERNAL_OWNER, false, b"EX12");
803 let (_signer_backing, signer) =
804 make_account([3; 32], Address::new_from_array([9; 32]), true, b"");
805 let (_raw_backing, raw) =
806 make_account([4; 32], Address::new_from_array([9; 32]), false, b"");
807
808 let declared_accounts = [declared];
809 let remaining_accounts = [external, signer, raw];
810 let mut typed = RemainingAccounts::strict(&declared_accounts, &remaining_accounts).typed();
811
812 let external = typed.next_external::<SampleExternal>().unwrap();
813 assert_eq!(external.key(), remaining_accounts[0].address());
814 let signer = typed.next_signer().unwrap();
815 assert_eq!(signer.key(), remaining_accounts[1].address());
816 let raw = typed.next_unchecked().unwrap();
817 assert_eq!(raw.key(), remaining_accounts[2].address());
818 assert!(typed.assert_empty().is_ok());
819 }
820 #[test]
821 fn typed_remaining_supports_groups_and_lazy_external_access() {
822 let (_declared_backing, declared) =
823 make_account([1; 32], Address::new_from_array([9; 32]), false, b"");
824 let (_external_a_backing, external_a) =
825 make_account([2; 32], EXTERNAL_OWNER, false, b"EX12");
826 let (_external_b_backing, external_b) =
827 make_account([3; 32], EXTERNAL_OWNER, false, b"EX34");
828 let (_signer_backing, signer) =
829 make_account([4; 32], Address::new_from_array([9; 32]), true, b"");
830
831 let declared_accounts = [declared];
832 let remaining_accounts = [external_a, external_b, signer];
833 let accounts = RemainingAccounts::strict(&declared_accounts, &remaining_accounts);
834
835 let lazy_external = accounts
836 .lazy()
837 .at(1)
838 .unwrap()
839 .external::<SampleExternal>()
840 .unwrap();
841 assert_eq!(lazy_external.key(), remaining_accounts[1].address());
842
843 let mut typed = accounts.typed().no_duplicates().unwrap();
844 let mut oracle_group = typed.take_group(2).unwrap();
845 let parsed = oracle_group.parse_external::<SampleExternal, 4>().unwrap();
846 assert_eq!(parsed.len(), 2);
847 assert_eq!(
848 parsed.get(0).unwrap().key(),
849 remaining_accounts[0].address()
850 );
851 assert_eq!(
852 parsed.get(1).unwrap().key(),
853 remaining_accounts[1].address()
854 );
855 assert!(oracle_group.assert_empty().is_ok());
856
857 let signer = typed.next_signer().unwrap();
858 assert_eq!(signer.key(), remaining_accounts[2].address());
859 assert!(typed.assert_empty().is_ok());
860 }
861 #[test]
862 fn typed_remaining_duplicate_and_sort_assertions_are_explicit() {
863 let (_declared_backing, declared) =
864 make_account([1; 32], Address::new_from_array([9; 32]), false, b"");
865 let (_duplicate_backing, duplicate) =
866 make_account([1; 32], Address::new_from_array([9; 32]), false, b"");
867 let declared_accounts = [declared];
868 let remaining_accounts = [duplicate];
869 let typed = RemainingAccounts::passthrough(&declared_accounts, &remaining_accounts).typed();
870 assert_eq!(
871 typed.assert_no_duplicates().unwrap_err(),
872 ProgramError::InvalidAccountData
873 );
874
875 let (_a_backing, a) = make_account([3; 32], Address::new_from_array([9; 32]), false, b"");
876 let (_b_backing, b) = make_account([2; 32], Address::new_from_array([9; 32]), false, b"");
877 let remaining_accounts = [a, b];
878 let typed = RemainingAccounts::passthrough(&[], &remaining_accounts).typed();
879 assert_eq!(
880 typed
881 .assert_sorted_by(|account| Ok(account.address().as_bytes()[0]))
882 .unwrap_err(),
883 ProgramError::InvalidAccountData
884 );
885 }
886}