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 ref_only;
60pub mod result;
61pub mod segment;
62pub mod tail;
63pub mod utils;
64pub mod zerocopy;
65#[doc(hidden)]
70pub use zerocopy::__sealed;
71pub mod context;
72pub mod instruction;
73pub mod layout;
74pub mod option_byte;
75pub mod pda;
76pub mod remaining;
77pub mod rent;
78pub mod segment_borrow;
79pub mod segment_lease;
80pub mod syscall;
81pub mod syscalls;
82pub mod system;
83pub mod token;
84pub mod token_2022_ext;
85
86pub use account::AccountView;
87pub use account_wrappers::{
88 Account, InitAccount, Interface, InterfaceAccount, InterfaceAccountLayout,
89 InterfaceAccountResolve, InterfaceSpec, Program, ProgramId, Signer as HopperSigner,
90 SystemAccount, SystemId, UncheckedAccount,
91};
92pub use address::Address;
93pub use audit::{AccountAudit, DuplicateAccount};
94pub use borrow::{Ref, RefMut};
95pub use context::Context;
96pub use cpi::{invoke, invoke_signed};
97pub use error::ProgramError;
98pub use field_map::{FieldInfo, FieldMap};
99pub use foreign::{ForeignLens, ForeignManifest};
100pub use interop::TransparentAddress;
101pub use migrate::{apply_pending_migrations, LayoutMigration, MigrationEdge};
102pub use policy::{HopperInstructionPolicy, HopperProgramPolicy};
103pub use ref_only::HopperRefOnly;
104pub use remaining::{
105 RemainingAccountViews, RemainingAccounts, RemainingError, RemainingMode, RemainingSigners,
106 MAX_REMAINING_ACCOUNTS,
107};
108pub use tail::{
109 borrow_address_slice, borrow_bounded_str, read_tail, read_tail_len, tail_capacity,
110 tail_payload, write_tail, BoundedString, BoundedVec, HopperString, HopperVec, TailCodec,
111 TailElement,
112};
113
114#[macro_export]
135macro_rules! layout_migrations {
136 ( $layout:ty = [ $( $edge:expr ),+ $(,)? ] $(,)? ) => {
137 impl $crate::migrate::LayoutMigration for $layout {
138 const MIGRATIONS: &'static [$crate::migrate::MigrationEdge] = &[
139 $( $edge ),+
140 ];
141 }
142 };
143}
144#[cfg(feature = "hopper-native-backend")]
145pub use instruction::CpiAccount;
146pub use instruction::{InstructionAccount, InstructionView, Seed, Signer};
147pub use layout::{HopperHeader, LayoutContract, LayoutInfo};
148pub use pod::Pod;
149pub use result::ProgramResult;
150pub use segment::{Segment, TypedSegment};
151pub use segment_borrow::{AccessKind, SegmentBorrow, SegmentBorrowGuard, SegmentBorrowRegistry};
152pub use segment_lease::{SegRef, SegRefMut, SegmentLease};
153pub use zerocopy::{AccountLayout, WireLayout, ZeroCopy};
154
155pub const MAX_TX_ACCOUNTS: usize = compat::BACKEND_MAX_TX_ACCOUNTS;
156pub const SUCCESS: u64 = compat::BACKEND_SUCCESS;
157
158#[cfg(feature = "hopper-native-backend")]
159#[doc(hidden)]
160pub use hopper_native as __hopper_native;
161
162#[cfg(feature = "solana-program-backend")]
163#[doc(hidden)]
164pub use ::solana_program as __solana_program;
165
166#[doc(hidden)]
167pub use five8_const as __five8_const;
168
169#[macro_export]
171macro_rules! address {
172 ( $literal:expr ) => {
173 $crate::Address::new_from_array($crate::__five8_const::decode_32_const($literal))
174 };
175}
176
177#[macro_export]
179macro_rules! require {
180 ( $cond:expr, $err:expr ) => {
181 if !($cond) {
182 return Err($err);
183 }
184 };
185 ( $cond:expr ) => {
186 if !($cond) {
187 return Err($crate::ProgramError::InvalidArgument);
188 }
189 };
190}
191
192#[macro_export]
194macro_rules! require_eq {
195 ( $left:expr, $right:expr, $err:expr ) => {
196 if ($left) != ($right) {
197 return Err($err);
198 }
199 };
200 ( $left:expr, $right:expr ) => {
201 if ($left) != ($right) {
202 return Err($crate::ProgramError::InvalidArgument);
203 }
204 };
205}
206
207#[macro_export]
211macro_rules! require_neq {
212 ( $left:expr, $right:expr, $err:expr ) => {
213 if ($left) == ($right) {
214 return Err($err);
215 }
216 };
217 ( $left:expr, $right:expr ) => {
218 if ($left) == ($right) {
219 return Err($crate::ProgramError::InvalidArgument);
220 }
221 };
222}
223
224#[macro_export]
237macro_rules! require_keys_eq {
238 ( $left:expr, $right:expr, $err:expr ) => {
239 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
240 != ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
241 {
242 return Err($err);
243 }
244 };
245 ( $left:expr, $right:expr ) => {
246 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
247 != ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
248 {
249 return Err($crate::ProgramError::InvalidAccountData);
250 }
251 };
252}
253
254#[macro_export]
258macro_rules! require_keys_neq {
259 ( $left:expr, $right:expr, $err:expr ) => {
260 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
261 == ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
262 {
263 return Err($err);
264 }
265 };
266 ( $left:expr, $right:expr ) => {
267 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
268 == ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
269 {
270 return Err($crate::ProgramError::InvalidAccountData);
271 }
272 };
273}
274
275#[macro_export]
278macro_rules! require_gte {
279 ( $left:expr, $right:expr, $err:expr ) => {
280 if !($left >= $right) {
281 return Err($err);
282 }
283 };
284 ( $left:expr, $right:expr ) => {
285 if !($left >= $right) {
286 return Err($crate::ProgramError::InsufficientFunds);
287 }
288 };
289}
290
291#[macro_export]
293macro_rules! require_gt {
294 ( $left:expr, $right:expr, $err:expr ) => {
295 if !($left > $right) {
296 return Err($err);
297 }
298 };
299 ( $left:expr, $right:expr ) => {
300 if !($left > $right) {
301 return Err($crate::ProgramError::InvalidArgument);
302 }
303 };
304}
305
306#[macro_export]
310macro_rules! require_lt {
311 ( $left:expr, $right:expr, $err:expr ) => {
312 if !($left < $right) {
313 return Err($err);
314 }
315 };
316 ( $left:expr, $right:expr ) => {
317 if !($left < $right) {
318 return Err($crate::ProgramError::InvalidArgument);
319 }
320 };
321}
322
323#[macro_export]
325macro_rules! require_lte {
326 ( $left:expr, $right:expr, $err:expr ) => {
327 if !($left <= $right) {
328 return Err($err);
329 }
330 };
331 ( $left:expr, $right:expr ) => {
332 if !($left <= $right) {
333 return Err($crate::ProgramError::InvalidArgument);
334 }
335 };
336}
337
338#[macro_export]
351macro_rules! err {
352 ( $e:expr ) => {
353 return ::core::result::Result::Err($crate::ProgramError::from($e))
354 };
355}
356
357#[macro_export]
360macro_rules! error {
361 ( $e:expr ) => {
362 return ::core::result::Result::Err($crate::ProgramError::from($e))
363 };
364}
365
366#[macro_export]
388macro_rules! hopper_unsafe_region {
389 ( $label:literal, $body:block ) => {{
390 const _HOPPER_UNSAFE_REGION_LABEL: &str = $label;
394 #[allow(unused_unsafe)]
395 unsafe { $body }
397 }};
398}
399
400#[macro_export]
402macro_rules! msg {
403 ( $literal:expr ) => {{
404 $crate::log::log($literal);
405 }};
406 ( $fmt:expr, $($arg:tt)* ) => {{
407 #[cfg(target_os = "solana")]
408 {
409 use core::fmt::Write;
410 let mut buf = [0u8; 256];
411 let mut wrapper = $crate::log::StackWriter::new(&mut buf);
412 let _ = write!(wrapper, $fmt, $($arg)*);
413 let len = wrapper.pos();
414 $crate::log::log(
415 unsafe { core::str::from_utf8_unchecked(&buf[..len]) }
417 );
418 }
419 #[cfg(not(target_os = "solana"))]
420 {
421 let _ = ($fmt, $($arg)*);
422 }
423 }};
424}
425
426#[macro_export]
465macro_rules! hopper_emit_cpi {
466 ( $program_id:expr, $event_authority:expr, $bump:expr, $event:expr ) => {{
467 let __ev = $event;
472 let __tag: u8 = ::core::convert::Into::<u8>::into(__ev.tag());
473 let __payload: &[u8] = __ev.as_bytes();
474 let mut __buf = [0u8; 2 + 1 + 512];
475 let __n = $crate::cpi_event::encode_event_cpi(__tag, __payload, &mut __buf[..])
476 .ok_or($crate::ProgramError::InvalidInstructionData)?;
477 let __bump_byte: [u8; 1] = [$bump];
480 let __seed_slices: [&[u8]; 2] = [b"__hopper_event_authority", &__bump_byte[..]];
481 $crate::cpi_event::invoke_event_cpi(
482 $program_id,
483 $event_authority,
484 &__buf[..__n],
485 &__seed_slices[..],
486 )?;
487 }};
488}
489
490#[macro_export]
511macro_rules! hopper_log {
512 ($label:expr, $a:expr) => {{
515 $crate::log::log($label);
516 $crate::log::log_64($a as u64, 0, 0, 0, 0);
517 }};
518 ($label:expr, $a:expr, $b:expr) => {{
519 $crate::log::log($label);
520 $crate::log::log_64($a as u64, $b as u64, 0, 0, 0);
521 }};
522 ($label:expr, $a:expr, $b:expr, $c:expr) => {{
523 $crate::log::log($label);
524 $crate::log::log_64($a as u64, $b as u64, $c as u64, 0, 0);
525 }};
526 ($label:expr, $a:expr, $b:expr, $c:expr, $d:expr) => {{
527 $crate::log::log($label);
528 $crate::log::log_64($a as u64, $b as u64, $c as u64, $d as u64, 0);
529 }};
530 ($label:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {{
531 $crate::log::log($label);
532 $crate::log::log_64($a as u64, $b as u64, $c as u64, $d as u64, $e as u64);
533 }};
534 ($msg:expr) => {{
536 $crate::log::log($msg);
537 }};
538}
539
540#[macro_export]
545macro_rules! hopper_entrypoint {
546 ( $process_instruction:expr ) => {
547 $crate::hopper_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
548 };
549 ( $process_instruction:expr, $maximum:expr ) => {
550 #[cfg(feature = "hopper-native-backend")]
551 #[no_mangle]
555 pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
556 const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
557 core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
558 let mut accounts = [UNINIT; $maximum];
559
560 let (program_id, count, instruction_data) = unsafe {
562 $crate::__hopper_native::raw_input::deserialize_accounts::<$maximum>(
563 input,
564 &mut accounts,
565 )
566 };
567
568 let hopper_program_id = unsafe {
570 &*(&program_id as *const $crate::__hopper_native::Address as *const $crate::Address)
571 };
572 let hopper_accounts = unsafe {
574 core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
575 };
576
577 match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
578 Ok(()) => $crate::__hopper_native::SUCCESS,
579 Err(error) => error.into(),
580 }
581 }
582
583 #[cfg(any(
584 feature = "legacy-pinocchio-compat",
585 feature = "solana-program-backend"
586 ))]
587 $crate::__hopper_compat_entrypoint!($process_instruction, $maximum);
588 };
589}
590
591#[macro_export]
593macro_rules! program_entrypoint {
594 ( $process_instruction:expr ) => {
595 $crate::hopper_entrypoint!($process_instruction);
596 };
597 ( $process_instruction:expr, $maximum:expr ) => {
598 $crate::hopper_entrypoint!($process_instruction, $maximum);
599 };
600}
601
602#[macro_export]
608macro_rules! hopper_fast_entrypoint {
609 ( $process_instruction:expr ) => {
610 $crate::hopper_fast_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
611 };
612 ( $process_instruction:expr, $maximum:expr ) => {
613 #[cfg(feature = "hopper-native-backend")]
614 #[no_mangle]
620 pub unsafe extern "C" fn entrypoint(input: *mut u8, ix_data: *const u8) -> u64 {
621 const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
622 core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
623 let mut accounts = [UNINIT; $maximum];
624
625 let ix_len = unsafe { *(ix_data.sub(8) as *const u64) as usize };
627 let instruction_data: &'static [u8] =
628 unsafe { core::slice::from_raw_parts(ix_data, ix_len) };
629 let program_id = unsafe {
631 core::ptr::read(ix_data.add(ix_len) as *const $crate::__hopper_native::Address)
632 };
633
634 let (program_id, count, instruction_data) = unsafe {
636 $crate::__hopper_native::raw_input::deserialize_accounts_fast::<$maximum>(
637 input,
638 &mut accounts,
639 instruction_data,
640 program_id,
641 )
642 };
643
644 let hopper_program_id = unsafe {
646 &*(&program_id as *const $crate::__hopper_native::Address as *const $crate::Address)
647 };
648 let hopper_accounts = unsafe {
650 core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
651 };
652
653 match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
654 Ok(()) => $crate::__hopper_native::SUCCESS,
655 Err(error) => error.into(),
656 }
657 }
658
659 #[cfg(any(
660 feature = "legacy-pinocchio-compat",
661 feature = "solana-program-backend"
662 ))]
663 compile_error!("hopper_fast_entrypoint! requires hopper-native-backend");
664 };
665}
666
667#[macro_export]
669macro_rules! fast_entrypoint {
670 ( $process_instruction:expr ) => {
671 $crate::hopper_fast_entrypoint!($process_instruction);
672 };
673 ( $process_instruction:expr, $maximum:expr ) => {
674 $crate::hopper_fast_entrypoint!($process_instruction, $maximum);
675 };
676}
677
678#[macro_export]
680macro_rules! hopper_lazy_entrypoint {
681 ( $process:expr ) => {
682 #[cfg(feature = "hopper-native-backend")]
683 $crate::__hopper_native::hopper_lazy_entrypoint!($process);
684
685 #[cfg(any(
686 feature = "legacy-pinocchio-compat",
687 feature = "solana-program-backend"
688 ))]
689 compile_error!("hopper_lazy_entrypoint! requires hopper-native-backend");
690 };
691}
692
693#[macro_export]
695macro_rules! lazy_entrypoint {
696 ( $process:expr ) => {
697 $crate::hopper_lazy_entrypoint!($process);
698 };
699}
700
701#[macro_export]
702macro_rules! no_allocator {
703 () => {
704 #[cfg(target_os = "solana")]
705 mod __hopper_allocator {
706 struct NoAlloc;
707
708 unsafe impl core::alloc::GlobalAlloc for NoAlloc {
709 unsafe fn alloc(&self, _layout: core::alloc::Layout) -> *mut u8 {
710 core::ptr::null_mut()
711 }
712
713 unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {}
714 }
715
716 #[global_allocator]
717 static ALLOCATOR: NoAlloc = NoAlloc;
718 }
719 };
720}
721
722#[macro_export]
723macro_rules! nostd_panic_handler {
724 () => {
725 #[cfg(target_os = "solana")]
726 #[panic_handler]
727 fn panic(_info: &core::panic::PanicInfo) -> ! {
728 let _ = _info;
729 loop {
730 core::hint::spin_loop();
731 }
732 }
733 };
734}