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(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"),
21))]
22compile_error!(
23 "Only one backend feature may be enabled at a time: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend"
24);
25
26#[cfg(not(any(
27 feature = "hopper-native-backend",
28 feature = "legacy-pinocchio-compat",
29 feature = "solana-program-backend",
30)))]
31compile_error!(
32 "At least one backend feature must be enabled: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend"
33);
34
35#[doc(hidden)]
36pub mod compat;
37
38pub mod error;
39pub mod result;
40pub mod address;
41pub mod account;
42pub mod account_wrappers;
43pub mod audit;
44pub mod borrow;
45pub(crate) mod borrow_registry;
46pub mod cpi;
47pub mod cpi_event;
48pub mod crank;
49pub mod dyn_cpi;
50pub mod utils;
51pub mod field_map;
52pub mod foreign;
53pub mod migrate;
54pub mod tail;
55pub mod interop;
56pub mod log;
57pub mod pod;
58pub mod segment;
59pub mod zerocopy;
60pub mod policy;
61pub mod ref_only;
62#[doc(hidden)]
67pub use zerocopy::__sealed;
68pub mod segment_borrow;
69pub mod segment_lease;
70pub mod instruction;
71pub mod layout;
72pub mod context;
73pub mod pda;
74pub mod rent;
75pub mod syscall;
76pub mod syscalls;
77pub mod system;
78pub mod option_byte;
79pub mod remaining;
80pub mod token;
81pub mod token_2022_ext;
82
83pub use account::{AccountView, RemainingAccounts};
84pub use account_wrappers::{Account, InitAccount, Program, ProgramId, Signer as HopperSigner, SystemId};
85pub use address::Address;
86pub use audit::{AccountAudit, DuplicateAccount};
87pub use borrow::{Ref, RefMut};
88pub use policy::{HopperInstructionPolicy, HopperProgramPolicy};
89pub use ref_only::HopperRefOnly;
90pub use context::Context;
91pub use cpi::{invoke, invoke_signed};
92pub use error::ProgramError;
93pub use field_map::{FieldInfo, FieldMap};
94pub use foreign::{ForeignLens, ForeignManifest};
95pub use interop::TransparentAddress;
96pub use migrate::{apply_pending_migrations, LayoutMigration, MigrationEdge};
97pub use tail::{read_tail, read_tail_len, tail_payload, write_tail, TailCodec};
98
99#[macro_export]
120macro_rules! layout_migrations {
121 ( $layout:ty = [ $( $edge:expr ),+ $(,)? ] $(,)? ) => {
122 impl $crate::migrate::LayoutMigration for $layout {
123 const MIGRATIONS: &'static [$crate::migrate::MigrationEdge] = &[
124 $( $edge ),+
125 ];
126 }
127 };
128}
129#[cfg(feature = "hopper-native-backend")]
130pub use instruction::CpiAccount;
131pub use instruction::{InstructionAccount, InstructionView, Seed, Signer};
132pub use layout::{HopperHeader, LayoutContract, LayoutInfo};
133pub use result::ProgramResult;
134pub use pod::Pod;
135pub use zerocopy::{AccountLayout, WireLayout, ZeroCopy};
136pub use segment::{Segment, TypedSegment};
137pub use segment_borrow::{AccessKind, SegmentBorrow, SegmentBorrowGuard, SegmentBorrowRegistry};
138pub use segment_lease::{SegRef, SegRefMut, SegmentLease};
139
140pub const MAX_TX_ACCOUNTS: usize = compat::BACKEND_MAX_TX_ACCOUNTS;
141pub const SUCCESS: u64 = compat::BACKEND_SUCCESS;
142
143#[cfg(feature = "hopper-native-backend")]
144#[doc(hidden)]
145pub use hopper_native as __hopper_native;
146
147#[cfg(feature = "solana-program-backend")]
148#[doc(hidden)]
149pub use ::solana_program as __solana_program;
150
151#[doc(hidden)]
152pub use five8_const as __five8_const;
153
154#[macro_export]
156macro_rules! address {
157 ( $literal:expr ) => {
158 $crate::Address::new_from_array($crate::__five8_const::decode_32_const($literal))
159 };
160}
161
162#[macro_export]
164macro_rules! require {
165 ( $cond:expr, $err:expr ) => {
166 if !($cond) { return Err($err); }
167 };
168 ( $cond:expr ) => {
169 if !($cond) { return Err($crate::ProgramError::InvalidArgument); }
170 };
171}
172
173#[macro_export]
175macro_rules! require_eq {
176 ( $left:expr, $right:expr, $err:expr ) => {
177 if ($left) != ($right) { return Err($err); }
178 };
179 ( $left:expr, $right:expr ) => {
180 if ($left) != ($right) { return Err($crate::ProgramError::InvalidArgument); }
181 };
182}
183
184#[macro_export]
188macro_rules! require_neq {
189 ( $left:expr, $right:expr, $err:expr ) => {
190 if ($left) == ($right) { return Err($err); }
191 };
192 ( $left:expr, $right:expr ) => {
193 if ($left) == ($right) { return Err($crate::ProgramError::InvalidArgument); }
194 };
195}
196
197#[macro_export]
210macro_rules! require_keys_eq {
211 ( $left:expr, $right:expr, $err:expr ) => {
212 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
213 != ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
214 {
215 return Err($err);
216 }
217 };
218 ( $left:expr, $right:expr ) => {
219 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
220 != ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
221 {
222 return Err($crate::ProgramError::InvalidAccountData);
223 }
224 };
225}
226
227#[macro_export]
231macro_rules! require_keys_neq {
232 ( $left:expr, $right:expr, $err:expr ) => {
233 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
234 == ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
235 {
236 return Err($err);
237 }
238 };
239 ( $left:expr, $right:expr ) => {
240 if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
241 == ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
242 {
243 return Err($crate::ProgramError::InvalidAccountData);
244 }
245 };
246}
247
248#[macro_export]
251macro_rules! require_gte {
252 ( $left:expr, $right:expr, $err:expr ) => {
253 if !($left >= $right) { return Err($err); }
254 };
255 ( $left:expr, $right:expr ) => {
256 if !($left >= $right) { return Err($crate::ProgramError::InsufficientFunds); }
257 };
258}
259
260#[macro_export]
262macro_rules! require_gt {
263 ( $left:expr, $right:expr, $err:expr ) => {
264 if !($left > $right) { return Err($err); }
265 };
266 ( $left:expr, $right:expr ) => {
267 if !($left > $right) { return Err($crate::ProgramError::InvalidArgument); }
268 };
269}
270
271#[macro_export]
275macro_rules! require_lt {
276 ( $left:expr, $right:expr, $err:expr ) => {
277 if !($left < $right) { return Err($err); }
278 };
279 ( $left:expr, $right:expr ) => {
280 if !($left < $right) { return Err($crate::ProgramError::InvalidArgument); }
281 };
282}
283
284#[macro_export]
286macro_rules! require_lte {
287 ( $left:expr, $right:expr, $err:expr ) => {
288 if !($left <= $right) { return Err($err); }
289 };
290 ( $left:expr, $right:expr ) => {
291 if !($left <= $right) { return Err($crate::ProgramError::InvalidArgument); }
292 };
293}
294
295#[macro_export]
308macro_rules! err {
309 ( $e:expr ) => {
310 return ::core::result::Result::Err($crate::ProgramError::from($e))
311 };
312}
313
314#[macro_export]
317macro_rules! error {
318 ( $e:expr ) => {
319 return ::core::result::Result::Err($crate::ProgramError::from($e))
320 };
321}
322
323#[macro_export]
345macro_rules! hopper_unsafe_region {
346 ( $label:literal, $body:block ) => {{
347 const _HOPPER_UNSAFE_REGION_LABEL: &str = $label;
351 #[allow(unused_unsafe)]
352 unsafe { $body }
353 }};
354}
355
356#[macro_export]
358macro_rules! msg {
359 ( $literal:expr ) => {{
360 $crate::log::log($literal);
361 }};
362 ( $fmt:expr, $($arg:tt)* ) => {{
363 #[cfg(target_os = "solana")]
364 {
365 use core::fmt::Write;
366 let mut buf = [0u8; 256];
367 let mut wrapper = $crate::log::StackWriter::new(&mut buf);
368 let _ = write!(wrapper, $fmt, $($arg)*);
369 let len = wrapper.pos();
370 $crate::log::log(
371 unsafe { core::str::from_utf8_unchecked(&buf[..len]) }
372 );
373 }
374 #[cfg(not(target_os = "solana"))]
375 {
376 let _ = ($fmt, $($arg)*);
377 }
378 }};
379}
380
381#[macro_export]
420macro_rules! hopper_emit_cpi {
421 ( $program_id:expr, $event_authority:expr, $bump:expr, $event:expr ) => {{
422 let __ev = $event;
427 let __tag: u8 = ::core::convert::Into::<u8>::into(__ev.tag());
428 let __payload: &[u8] = __ev.as_bytes();
429 let mut __buf = [0u8; 2 + 1 + 512];
430 let __n = $crate::cpi_event::encode_event_cpi(__tag, __payload, &mut __buf[..])
431 .ok_or($crate::ProgramError::InvalidInstructionData)?;
432 let __bump_byte: [u8; 1] = [$bump];
435 let __seed_slices: [&[u8]; 2] = [b"__hopper_event_authority", &__bump_byte[..]];
436 $crate::cpi_event::invoke_event_cpi(
437 $program_id,
438 $event_authority,
439 &__buf[..__n],
440 &__seed_slices[..],
441 )?;
442 }};
443}
444
445#[macro_export]
466macro_rules! hopper_log {
467 ($label:expr, $a:expr) => {{
470 $crate::log::log($label);
471 $crate::log::log_64($a as u64, 0, 0, 0, 0);
472 }};
473 ($label:expr, $a:expr, $b:expr) => {{
474 $crate::log::log($label);
475 $crate::log::log_64($a as u64, $b as u64, 0, 0, 0);
476 }};
477 ($label:expr, $a:expr, $b:expr, $c:expr) => {{
478 $crate::log::log($label);
479 $crate::log::log_64($a as u64, $b as u64, $c as u64, 0, 0);
480 }};
481 ($label:expr, $a:expr, $b:expr, $c:expr, $d:expr) => {{
482 $crate::log::log($label);
483 $crate::log::log_64($a as u64, $b as u64, $c as u64, $d as u64, 0);
484 }};
485 ($label:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {{
486 $crate::log::log($label);
487 $crate::log::log_64(
488 $a as u64, $b as u64, $c as u64, $d as u64, $e as u64,
489 );
490 }};
491 ($msg:expr) => {{
493 $crate::log::log($msg);
494 }};
495}
496
497#[macro_export]
502macro_rules! hopper_entrypoint {
503 ( $process_instruction:expr ) => {
504 $crate::hopper_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
505 };
506 ( $process_instruction:expr, $maximum:expr ) => {
507 #[cfg(feature = "hopper-native-backend")]
508 #[no_mangle]
512 pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
513 const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
514 core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
515 let mut accounts = [UNINIT; $maximum];
516
517 let (program_id, count, instruction_data) = unsafe {
518 $crate::__hopper_native::raw_input::deserialize_accounts::<$maximum>(
519 input,
520 &mut accounts,
521 )
522 };
523
524 let hopper_program_id = unsafe {
525 &*(
526 &program_id as *const $crate::__hopper_native::Address
527 as *const $crate::Address
528 )
529 };
530 let hopper_accounts = unsafe {
531 core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
532 };
533
534 match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
535 Ok(()) => $crate::__hopper_native::SUCCESS,
536 Err(error) => error.into(),
537 }
538 }
539
540 #[cfg(any(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"))]
541 $crate::__hopper_compat_entrypoint!($process_instruction, $maximum);
542 };
543}
544
545#[macro_export]
547macro_rules! program_entrypoint {
548 ( $process_instruction:expr ) => {
549 $crate::hopper_entrypoint!($process_instruction);
550 };
551 ( $process_instruction:expr, $maximum:expr ) => {
552 $crate::hopper_entrypoint!($process_instruction, $maximum);
553 };
554}
555
556#[macro_export]
562macro_rules! hopper_fast_entrypoint {
563 ( $process_instruction:expr ) => {
564 $crate::hopper_fast_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
565 };
566 ( $process_instruction:expr, $maximum:expr ) => {
567 #[cfg(feature = "hopper-native-backend")]
568 #[no_mangle]
574 pub unsafe extern "C" fn entrypoint(input: *mut u8, ix_data: *const u8) -> u64 {
575 const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
576 core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
577 let mut accounts = [UNINIT; $maximum];
578
579 let ix_len = unsafe { *(ix_data.sub(8) as *const u64) as usize };
580 let instruction_data: &'static [u8] =
581 unsafe { core::slice::from_raw_parts(ix_data, ix_len) };
582 let program_id = unsafe {
583 core::ptr::read(ix_data.add(ix_len) as *const $crate::__hopper_native::Address)
584 };
585
586 let (program_id, count, instruction_data) = unsafe {
587 $crate::__hopper_native::raw_input::deserialize_accounts_fast::<$maximum>(
588 input,
589 &mut accounts,
590 instruction_data,
591 program_id,
592 )
593 };
594
595 let hopper_program_id = unsafe {
596 &*(
597 &program_id as *const $crate::__hopper_native::Address
598 as *const $crate::Address
599 )
600 };
601 let hopper_accounts = unsafe {
602 core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
603 };
604
605 match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
606 Ok(()) => $crate::__hopper_native::SUCCESS,
607 Err(error) => error.into(),
608 }
609 }
610
611 #[cfg(any(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"))]
612 compile_error!("hopper_fast_entrypoint! requires hopper-native-backend");
613 };
614}
615
616#[macro_export]
618macro_rules! fast_entrypoint {
619 ( $process_instruction:expr ) => {
620 $crate::hopper_fast_entrypoint!($process_instruction);
621 };
622 ( $process_instruction:expr, $maximum:expr ) => {
623 $crate::hopper_fast_entrypoint!($process_instruction, $maximum);
624 };
625}
626
627#[macro_export]
629macro_rules! hopper_lazy_entrypoint {
630 ( $process:expr ) => {
631 #[cfg(feature = "hopper-native-backend")]
632 $crate::__hopper_native::hopper_lazy_entrypoint!($process);
633
634 #[cfg(any(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"))]
635 compile_error!("hopper_lazy_entrypoint! requires hopper-native-backend");
636 };
637}
638
639#[macro_export]
641macro_rules! lazy_entrypoint {
642 ( $process:expr ) => {
643 $crate::hopper_lazy_entrypoint!($process);
644 };
645}
646
647#[macro_export]
648macro_rules! no_allocator {
649 () => {
650 #[cfg(target_os = "solana")]
651 mod __hopper_allocator {
652 struct NoAlloc;
653
654 unsafe impl core::alloc::GlobalAlloc for NoAlloc {
655 unsafe fn alloc(&self, _layout: core::alloc::Layout) -> *mut u8 {
656 core::ptr::null_mut()
657 }
658
659 unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {}
660 }
661
662 #[global_allocator]
663 static ALLOCATOR: NoAlloc = NoAlloc;
664 }
665 };
666}
667
668#[macro_export]
669macro_rules! nostd_panic_handler {
670 () => {
671 #[cfg(target_os = "solana")]
672 #[panic_handler]
673 fn panic(_info: &core::panic::PanicInfo) -> ! {
674 let _ = _info;
675 loop {
676 core::hint::spin_loop();
677 }
678 }
679 };
680}