1#![no_std]
9#![deny(unsafe_op_in_unsafe_fn)]
10
11#[cfg(test)]
12extern crate std;
13
14#[cfg(feature = "solana-program-backend")]
15extern crate alloc;
16
17#[cfg(any(
18 all(feature = "hopper-native-backend", feature = "legacy-pinocchio-compat"),
19 all(feature = "hopper-native-backend", feature = "solana-program-backend"),
20 all(
21 feature = "legacy-pinocchio-compat",
22 feature = "solana-program-backend"
23 ),
24))]
25compile_error!(
26 "Only one backend feature may be enabled at a time: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend"
27);
28
29#[cfg(not(any(
30 feature = "hopper-native-backend",
31 feature = "legacy-pinocchio-compat",
32 feature = "solana-program-backend",
33)))]
34compile_error!(
35 "At least one backend feature must be enabled: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend"
36);
37
38#[doc(hidden)]
39pub mod compat;
40
41pub mod account;
42pub mod account_wrappers;
43pub mod address;
44pub mod audit;
45pub mod borrow;
46pub(crate) mod borrow_registry;
47pub mod cpi;
48pub mod cpi_event;
49pub mod crank;
50pub mod dyn_cpi;
51pub mod error;
52pub mod field_map;
53pub mod foreign;
54pub mod interop;
55pub mod log;
56pub mod migrate;
57pub mod pod;
58pub mod policy;
59pub mod proof;
60pub mod ref_only;
61pub mod result;
62pub mod segment;
63pub mod tail;
64pub mod utils;
65pub mod zerocopy;
66#[doc(hidden)]
71pub use zerocopy::__sealed;
72pub mod context;
73pub mod instruction;
74pub mod layout;
75pub mod option_byte;
76pub mod pda;
77pub mod remaining;
78pub mod rent;
79pub mod segment_borrow;
80pub mod segment_lease;
81pub mod syscall;
82pub mod syscalls;
83pub mod system;
84pub mod token;
85pub mod token_2022_ext;
86
87pub use account::AccountView;
88pub use account_wrappers::{
89 Account, InitAccount, Interface, InterfaceAccount, InterfaceAccountLayout,
90 InterfaceAccountResolve, InterfaceSpec, Program, ProgramId, Signer as HopperSigner,
91 SystemAccount, SystemId, UncheckedAccount,
92};
93pub use address::Address;
94pub use audit::{AccountAudit, DuplicateAccount};
95pub use borrow::{Ref, RefMut};
96pub use context::Context;
97pub use cpi::{invoke, invoke_signed};
98pub use error::ProgramError;
99pub use field_map::{FieldInfo, FieldMap};
100pub use foreign::{ForeignLens, ForeignManifest};
101pub use interop::TransparentAddress;
102pub use migrate::{apply_pending_migrations, LayoutMigration, MigrationEdge};
103pub use policy::{HopperInstructionPolicy, HopperProgramPolicy, HopperProgramProfile};
104pub use proof::{
105 AccountProof, ExecutableChecked, HasOneChecked, LayoutChecked, OwnerChecked, SeedsChecked,
106 SignerChecked, TokenExtensionsChecked, Unchecked, WritableChecked,
107};
108pub use ref_only::HopperRefOnly;
109pub use remaining::{
110 RemainingAccountViews, RemainingAccounts, RemainingError, RemainingMode, RemainingSigners,
111 MAX_REMAINING_ACCOUNTS,
112};
113pub use tail::{
114 borrow_address_slice, borrow_bounded_str, read_tail, read_tail_len, tail_capacity,
115 tail_payload, write_tail, BoundedString, BoundedVec, HopperString, HopperVec, TailCodec,
116 TailElement,
117};
118
119#[macro_export]
140macro_rules! layout_migrations {
141 ( $layout:ty = [ $( $edge:expr ),+ $(,)? ] $(,)? ) => {
142 impl $crate::migrate::LayoutMigration for $layout {
143 const MIGRATIONS: &'static [$crate::migrate::MigrationEdge] = &[
144 $( $edge ),+
145 ];
146 }
147 };
148}
149#[cfg(feature = "hopper-native-backend")]
150pub use instruction::CpiAccount;
151pub use instruction::{InstructionAccount, InstructionView, Seed, Signer};
152pub use layout::{HopperHeader, LayoutContract, LayoutInfo};
153pub use pod::Pod;
154pub use result::ProgramResult;
155pub use segment::{
156 FieldCapability, Segment, TypedSegment, FIELD_POLICY_AUTHORITY_GATED,
157 FIELD_POLICY_CHECKED_MATH, FIELD_POLICY_IMMUTABLE_AFTER_INIT, FIELD_ROLE_AUTHORITY,
158 FIELD_ROLE_BALANCE, FIELD_ROLE_DATA, FIELD_ROLE_VERSION,
159};
160pub use segment_borrow::{AccessKind, SegmentBorrow, SegmentBorrowGuard, SegmentBorrowRegistry};
161pub use segment_lease::{SegRef, SegRefMut, SegmentLease};
162pub use zerocopy::{AccountLayout, WireLayout, ZeroCopy};
163
164pub const MAX_TX_ACCOUNTS: usize = compat::BACKEND_MAX_TX_ACCOUNTS;
165pub const SUCCESS: u64 = compat::BACKEND_SUCCESS;
166
167#[cfg(feature = "hopper-native-backend")]
168#[doc(hidden)]
169pub use hopper_native as __hopper_native;
170
171#[cfg(feature = "solana-program-backend")]
172#[doc(hidden)]
173pub use ::solana_program as __solana_program;
174
175#[doc(hidden)]
176pub use five8_const as __five8_const;
177
178#[macro_export]
180macro_rules! address {
181 ( $literal:expr ) => {
182 $crate::Address::new_from_array($crate::__five8_const::decode_32_const($literal))
183 };
184}
185
186#[macro_export]
188macro_rules! require {
189 ( $cond:expr, $err:expr ) => {
190 if !($cond) {
191 return Err($err);
192 }
193 };
194 ( $cond:expr ) => {
195 if !($cond) {
196 return Err($crate::ProgramError::InvalidArgument);
197 }
198 };
199}
200
201#[macro_export]
203macro_rules! require_eq {
204 ( $left:expr, $right:expr, $err:expr ) => {
205 if ($left) != ($right) {
206 return Err($err);
207 }
208 };
209 ( $left:expr, $right:expr ) => {
210 if ($left) != ($right) {
211 return Err($crate::ProgramError::InvalidArgument);
212 }
213 };
214}
215
216#[macro_export]
220macro_rules! require_neq {
221 ( $left:expr, $right:expr, $err:expr ) => {
222 if ($left) == ($right) {
223 return Err($err);
224 }
225 };
226 ( $left:expr, $right:expr ) => {
227 if ($left) == ($right) {
228 return Err($crate::ProgramError::InvalidArgument);
229 }
230 };
231}
232
233#[macro_export]
246macro_rules! require_keys_eq {
247 ( $left:expr, $right:expr, $err:expr ) => {
248 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
249 != ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
250 {
251 return Err($err);
252 }
253 };
254 ( $left:expr, $right:expr ) => {
255 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
256 != ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
257 {
258 return Err($crate::ProgramError::InvalidAccountData);
259 }
260 };
261}
262
263#[macro_export]
267macro_rules! require_keys_neq {
268 ( $left:expr, $right:expr, $err:expr ) => {
269 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
270 == ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
271 {
272 return Err($err);
273 }
274 };
275 ( $left:expr, $right:expr ) => {
276 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
277 == ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
278 {
279 return Err($crate::ProgramError::InvalidAccountData);
280 }
281 };
282}
283
284#[macro_export]
287macro_rules! require_gte {
288 ( $left:expr, $right:expr, $err:expr ) => {
289 if !($left >= $right) {
290 return Err($err);
291 }
292 };
293 ( $left:expr, $right:expr ) => {
294 if !($left >= $right) {
295 return Err($crate::ProgramError::InsufficientFunds);
296 }
297 };
298}
299
300#[macro_export]
302macro_rules! require_gt {
303 ( $left:expr, $right:expr, $err:expr ) => {
304 if !($left > $right) {
305 return Err($err);
306 }
307 };
308 ( $left:expr, $right:expr ) => {
309 if !($left > $right) {
310 return Err($crate::ProgramError::InvalidArgument);
311 }
312 };
313}
314
315#[macro_export]
319macro_rules! require_lt {
320 ( $left:expr, $right:expr, $err:expr ) => {
321 if !($left < $right) {
322 return Err($err);
323 }
324 };
325 ( $left:expr, $right:expr ) => {
326 if !($left < $right) {
327 return Err($crate::ProgramError::InvalidArgument);
328 }
329 };
330}
331
332#[macro_export]
334macro_rules! require_lte {
335 ( $left:expr, $right:expr, $err:expr ) => {
336 if !($left <= $right) {
337 return Err($err);
338 }
339 };
340 ( $left:expr, $right:expr ) => {
341 if !($left <= $right) {
342 return Err($crate::ProgramError::InvalidArgument);
343 }
344 };
345}
346
347#[macro_export]
360macro_rules! err {
361 ( $e:expr ) => {
362 return ::core::result::Result::Err($crate::ProgramError::from($e))
363 };
364}
365
366#[macro_export]
369macro_rules! error {
370 ( $e:expr ) => {
371 return ::core::result::Result::Err($crate::ProgramError::from($e))
372 };
373}
374
375#[macro_export]
397macro_rules! hopper_unsafe_region {
398 ( $label:literal, $body:block ) => {{
399 const _HOPPER_UNSAFE_REGION_LABEL: &str = $label;
403 #[allow(unused_unsafe)]
404 unsafe { $body }
406 }};
407}
408
409#[macro_export]
411macro_rules! msg {
412 ( $literal:expr ) => {{
413 $crate::log::log($literal);
414 }};
415 ( $fmt:expr, $($arg:tt)* ) => {{
416 #[cfg(target_os = "solana")]
417 {
418 use core::fmt::Write;
419 let mut buf = [0u8; 256];
420 let mut wrapper = $crate::log::StackWriter::new(&mut buf);
421 let _ = write!(wrapper, $fmt, $($arg)*);
422 let len = wrapper.pos();
423 $crate::log::log(
424 unsafe { core::str::from_utf8_unchecked(&buf[..len]) }
426 );
427 }
428 #[cfg(not(target_os = "solana"))]
429 {
430 let _ = ($fmt, $($arg)*);
431 }
432 }};
433}
434
435#[macro_export]
474macro_rules! hopper_emit_cpi {
475 ( $program_id:expr, $event_authority:expr, $bump:expr, $event:expr ) => {{
476 let __ev = $event;
481 let __tag: u8 = ::core::convert::Into::<u8>::into(__ev.tag());
482 let __payload: &[u8] = __ev.as_bytes();
483 let mut __buf = [0u8; 2 + 1 + 512];
484 let __n = $crate::cpi_event::encode_event_cpi(__tag, __payload, &mut __buf[..])
485 .ok_or($crate::ProgramError::InvalidInstructionData)?;
486 let __bump_byte: [u8; 1] = [$bump];
489 let __seed_slices: [&[u8]; 2] = [b"__hopper_event_authority", &__bump_byte[..]];
490 $crate::cpi_event::invoke_event_cpi(
491 $program_id,
492 $event_authority,
493 &__buf[..__n],
494 &__seed_slices[..],
495 )?;
496 }};
497}
498
499#[macro_export]
520macro_rules! hopper_log {
521 ($label:expr, $a:expr) => {{
524 $crate::log::log($label);
525 $crate::log::log_64($a as u64, 0, 0, 0, 0);
526 }};
527 ($label:expr, $a:expr, $b:expr) => {{
528 $crate::log::log($label);
529 $crate::log::log_64($a as u64, $b as u64, 0, 0, 0);
530 }};
531 ($label:expr, $a:expr, $b:expr, $c:expr) => {{
532 $crate::log::log($label);
533 $crate::log::log_64($a as u64, $b as u64, $c as u64, 0, 0);
534 }};
535 ($label:expr, $a:expr, $b:expr, $c:expr, $d:expr) => {{
536 $crate::log::log($label);
537 $crate::log::log_64($a as u64, $b as u64, $c as u64, $d as u64, 0);
538 }};
539 ($label:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {{
540 $crate::log::log($label);
541 $crate::log::log_64($a as u64, $b as u64, $c as u64, $d as u64, $e as u64);
542 }};
543 ($msg:expr) => {{
545 $crate::log::log($msg);
546 }};
547}
548
549#[macro_export]
554macro_rules! hopper_entrypoint {
555 ( $process_instruction:expr ) => {
556 $crate::hopper_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
557 };
558 ( $process_instruction:expr, $maximum:expr ) => {
559 #[cfg(feature = "hopper-native-backend")]
560 #[no_mangle]
564 pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
565 const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
566 core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
567 let mut accounts = [UNINIT; $maximum];
568
569 let (program_id, count, instruction_data) = unsafe {
571 $crate::__hopper_native::raw_input::deserialize_accounts::<$maximum>(
572 input,
573 &mut accounts,
574 )
575 };
576
577 let hopper_program_id = unsafe {
579 &*(&program_id as *const $crate::__hopper_native::Address as *const $crate::Address)
580 };
581 let hopper_accounts = unsafe {
583 core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
584 };
585
586 match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
587 Ok(()) => $crate::__hopper_native::SUCCESS,
588 Err(error) => error.into(),
589 }
590 }
591
592 #[cfg(any(
593 feature = "legacy-pinocchio-compat",
594 feature = "solana-program-backend"
595 ))]
596 $crate::__hopper_compat_entrypoint!($process_instruction, $maximum);
597 };
598}
599
600#[macro_export]
602macro_rules! program_entrypoint {
603 ( $process_instruction:expr ) => {
604 $crate::hopper_entrypoint!($process_instruction);
605 };
606 ( $process_instruction:expr, $maximum:expr ) => {
607 $crate::hopper_entrypoint!($process_instruction, $maximum);
608 };
609}
610
611#[macro_export]
617macro_rules! hopper_fast_entrypoint {
618 ( $process_instruction:expr ) => {
619 $crate::hopper_fast_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
620 };
621 ( $process_instruction:expr, $maximum:expr ) => {
622 #[cfg(feature = "hopper-native-backend")]
623 #[no_mangle]
629 pub unsafe extern "C" fn entrypoint(input: *mut u8, ix_data: *const u8) -> u64 {
630 const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
631 core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
632 let mut accounts = [UNINIT; $maximum];
633
634 let ix_len = unsafe { *(ix_data.sub(8) as *const u64) as usize };
636 let instruction_data: &'static [u8] =
637 unsafe { core::slice::from_raw_parts(ix_data, ix_len) };
638 let program_id = unsafe {
640 core::ptr::read(ix_data.add(ix_len) as *const $crate::__hopper_native::Address)
641 };
642
643 let (program_id, count, instruction_data) = unsafe {
645 $crate::__hopper_native::raw_input::deserialize_accounts_fast::<$maximum>(
646 input,
647 &mut accounts,
648 instruction_data,
649 program_id,
650 )
651 };
652
653 let hopper_program_id = unsafe {
655 &*(&program_id as *const $crate::__hopper_native::Address as *const $crate::Address)
656 };
657 let hopper_accounts = unsafe {
659 core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
660 };
661
662 match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
663 Ok(()) => $crate::__hopper_native::SUCCESS,
664 Err(error) => error.into(),
665 }
666 }
667
668 #[cfg(any(
669 feature = "legacy-pinocchio-compat",
670 feature = "solana-program-backend"
671 ))]
672 compile_error!("hopper_fast_entrypoint! requires hopper-native-backend");
673 };
674}
675
676#[macro_export]
678macro_rules! fast_entrypoint {
679 ( $process_instruction:expr ) => {
680 $crate::hopper_fast_entrypoint!($process_instruction);
681 };
682 ( $process_instruction:expr, $maximum:expr ) => {
683 $crate::hopper_fast_entrypoint!($process_instruction, $maximum);
684 };
685}
686
687#[macro_export]
689macro_rules! hopper_lazy_entrypoint {
690 ( $process:expr ) => {
691 #[cfg(feature = "hopper-native-backend")]
692 $crate::__hopper_native::hopper_lazy_entrypoint!($process);
693
694 #[cfg(any(
695 feature = "legacy-pinocchio-compat",
696 feature = "solana-program-backend"
697 ))]
698 compile_error!("hopper_lazy_entrypoint! requires hopper-native-backend");
699 };
700}
701
702#[macro_export]
704macro_rules! lazy_entrypoint {
705 ( $process:expr ) => {
706 $crate::hopper_lazy_entrypoint!($process);
707 };
708}
709
710#[macro_export]
711macro_rules! no_allocator {
712 () => {
713 #[cfg(target_os = "solana")]
714 mod __hopper_allocator {
715 struct NoAlloc;
716
717 unsafe impl core::alloc::GlobalAlloc for NoAlloc {
718 unsafe fn alloc(&self, _layout: core::alloc::Layout) -> *mut u8 {
719 core::ptr::null_mut()
720 }
721
722 unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {}
723 }
724
725 #[global_allocator]
726 static ALLOCATOR: NoAlloc = NoAlloc;
727 }
728 };
729}
730
731#[macro_export]
732macro_rules! nostd_panic_handler {
733 () => {
734 #[cfg(target_os = "solana")]
735 #[panic_handler]
736 fn panic(_info: &core::panic::PanicInfo) -> ! {
737 let _ = _info;
738 loop {
739 core::hint::spin_loop();
740 }
741 }
742 };
743}