#![no_std]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(test)]
extern crate std;
#[cfg(feature = "solana-program-backend")]
extern crate alloc;
#[cfg(any(
all(feature = "hopper-native-backend", feature = "legacy-pinocchio-compat"),
all(feature = "hopper-native-backend", feature = "solana-program-backend"),
all(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"),
))]
compile_error!(
"Only one backend feature may be enabled at a time: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend"
);
#[cfg(not(any(
feature = "hopper-native-backend",
feature = "legacy-pinocchio-compat",
feature = "solana-program-backend",
)))]
compile_error!(
"At least one backend feature must be enabled: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend"
);
#[doc(hidden)]
pub mod compat;
pub mod error;
pub mod result;
pub mod address;
pub mod account;
pub mod account_wrappers;
pub mod audit;
pub mod borrow;
pub(crate) mod borrow_registry;
pub mod cpi;
pub mod cpi_event;
pub mod crank;
pub mod dyn_cpi;
pub mod utils;
pub mod field_map;
pub mod foreign;
pub mod migrate;
pub mod tail;
pub mod interop;
pub mod log;
pub mod pod;
pub mod segment;
pub mod zerocopy;
pub mod policy;
pub mod ref_only;
#[doc(hidden)]
pub use zerocopy::__sealed;
pub mod segment_borrow;
pub mod segment_lease;
pub mod instruction;
pub mod layout;
pub mod context;
pub mod pda;
pub mod rent;
pub mod syscall;
pub mod syscalls;
pub mod system;
pub mod option_byte;
pub mod remaining;
pub mod token;
pub mod token_2022_ext;
pub use account::{AccountView, RemainingAccounts};
pub use account_wrappers::{Account, InitAccount, Program, ProgramId, Signer as HopperSigner, SystemId};
pub use address::Address;
pub use audit::{AccountAudit, DuplicateAccount};
pub use borrow::{Ref, RefMut};
pub use policy::{HopperInstructionPolicy, HopperProgramPolicy};
pub use ref_only::HopperRefOnly;
pub use context::Context;
pub use cpi::{invoke, invoke_signed};
pub use error::ProgramError;
pub use field_map::{FieldInfo, FieldMap};
pub use foreign::{ForeignLens, ForeignManifest};
pub use interop::TransparentAddress;
pub use migrate::{apply_pending_migrations, LayoutMigration, MigrationEdge};
pub use tail::{read_tail, read_tail_len, tail_payload, write_tail, TailCodec};
#[macro_export]
macro_rules! layout_migrations {
( $layout:ty = [ $( $edge:expr ),+ $(,)? ] $(,)? ) => {
impl $crate::migrate::LayoutMigration for $layout {
const MIGRATIONS: &'static [$crate::migrate::MigrationEdge] = &[
$( $edge ),+
];
}
};
}
#[cfg(feature = "hopper-native-backend")]
pub use instruction::CpiAccount;
pub use instruction::{InstructionAccount, InstructionView, Seed, Signer};
pub use layout::{HopperHeader, LayoutContract, LayoutInfo};
pub use result::ProgramResult;
pub use pod::Pod;
pub use zerocopy::{AccountLayout, WireLayout, ZeroCopy};
pub use segment::{Segment, TypedSegment};
pub use segment_borrow::{AccessKind, SegmentBorrow, SegmentBorrowGuard, SegmentBorrowRegistry};
pub use segment_lease::{SegRef, SegRefMut, SegmentLease};
pub const MAX_TX_ACCOUNTS: usize = compat::BACKEND_MAX_TX_ACCOUNTS;
pub const SUCCESS: u64 = compat::BACKEND_SUCCESS;
#[cfg(feature = "hopper-native-backend")]
#[doc(hidden)]
pub use hopper_native as __hopper_native;
#[cfg(feature = "solana-program-backend")]
#[doc(hidden)]
pub use ::solana_program as __solana_program;
#[doc(hidden)]
pub use five8_const as __five8_const;
#[macro_export]
macro_rules! address {
( $literal:expr ) => {
$crate::Address::new_from_array($crate::__five8_const::decode_32_const($literal))
};
}
#[macro_export]
macro_rules! require {
( $cond:expr, $err:expr ) => {
if !($cond) { return Err($err); }
};
( $cond:expr ) => {
if !($cond) { return Err($crate::ProgramError::InvalidArgument); }
};
}
#[macro_export]
macro_rules! require_eq {
( $left:expr, $right:expr, $err:expr ) => {
if ($left) != ($right) { return Err($err); }
};
( $left:expr, $right:expr ) => {
if ($left) != ($right) { return Err($crate::ProgramError::InvalidArgument); }
};
}
#[macro_export]
macro_rules! require_neq {
( $left:expr, $right:expr, $err:expr ) => {
if ($left) == ($right) { return Err($err); }
};
( $left:expr, $right:expr ) => {
if ($left) == ($right) { return Err($crate::ProgramError::InvalidArgument); }
};
}
#[macro_export]
macro_rules! require_keys_eq {
( $left:expr, $right:expr, $err:expr ) => {
if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
!= ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
{
return Err($err);
}
};
( $left:expr, $right:expr ) => {
if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
!= ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
{
return Err($crate::ProgramError::InvalidAccountData);
}
};
}
#[macro_export]
macro_rules! require_keys_neq {
( $left:expr, $right:expr, $err:expr ) => {
if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
== ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
{
return Err($err);
}
};
( $left:expr, $right:expr ) => {
if ::core::convert::AsRef::<[u8; 32]>::as_ref(&$left)
== ::core::convert::AsRef::<[u8; 32]>::as_ref(&$right)
{
return Err($crate::ProgramError::InvalidAccountData);
}
};
}
#[macro_export]
macro_rules! require_gte {
( $left:expr, $right:expr, $err:expr ) => {
if !($left >= $right) { return Err($err); }
};
( $left:expr, $right:expr ) => {
if !($left >= $right) { return Err($crate::ProgramError::InsufficientFunds); }
};
}
#[macro_export]
macro_rules! require_gt {
( $left:expr, $right:expr, $err:expr ) => {
if !($left > $right) { return Err($err); }
};
( $left:expr, $right:expr ) => {
if !($left > $right) { return Err($crate::ProgramError::InvalidArgument); }
};
}
#[macro_export]
macro_rules! require_lt {
( $left:expr, $right:expr, $err:expr ) => {
if !($left < $right) { return Err($err); }
};
( $left:expr, $right:expr ) => {
if !($left < $right) { return Err($crate::ProgramError::InvalidArgument); }
};
}
#[macro_export]
macro_rules! require_lte {
( $left:expr, $right:expr, $err:expr ) => {
if !($left <= $right) { return Err($err); }
};
( $left:expr, $right:expr ) => {
if !($left <= $right) { return Err($crate::ProgramError::InvalidArgument); }
};
}
#[macro_export]
macro_rules! err {
( $e:expr ) => {
return ::core::result::Result::Err($crate::ProgramError::from($e))
};
}
#[macro_export]
macro_rules! error {
( $e:expr ) => {
return ::core::result::Result::Err($crate::ProgramError::from($e))
};
}
#[macro_export]
macro_rules! hopper_unsafe_region {
( $label:literal, $body:block ) => {{
const _HOPPER_UNSAFE_REGION_LABEL: &str = $label;
#[allow(unused_unsafe)]
unsafe { $body }
}};
}
#[macro_export]
macro_rules! msg {
( $literal:expr ) => {{
$crate::log::log($literal);
}};
( $fmt:expr, $($arg:tt)* ) => {{
#[cfg(target_os = "solana")]
{
use core::fmt::Write;
let mut buf = [0u8; 256];
let mut wrapper = $crate::log::StackWriter::new(&mut buf);
let _ = write!(wrapper, $fmt, $($arg)*);
let len = wrapper.pos();
$crate::log::log(
unsafe { core::str::from_utf8_unchecked(&buf[..len]) }
);
}
#[cfg(not(target_os = "solana"))]
{
let _ = ($fmt, $($arg)*);
}
}};
}
#[macro_export]
macro_rules! hopper_emit_cpi {
( $program_id:expr, $event_authority:expr, $bump:expr, $event:expr ) => {{
let __ev = $event;
let __tag: u8 = ::core::convert::Into::<u8>::into(__ev.tag());
let __payload: &[u8] = __ev.as_bytes();
let mut __buf = [0u8; 2 + 1 + 512];
let __n = $crate::cpi_event::encode_event_cpi(__tag, __payload, &mut __buf[..])
.ok_or($crate::ProgramError::InvalidInstructionData)?;
let __bump_byte: [u8; 1] = [$bump];
let __seed_slices: [&[u8]; 2] = [b"__hopper_event_authority", &__bump_byte[..]];
$crate::cpi_event::invoke_event_cpi(
$program_id,
$event_authority,
&__buf[..__n],
&__seed_slices[..],
)?;
}};
}
#[macro_export]
macro_rules! hopper_log {
($label:expr, $a:expr) => {{
$crate::log::log($label);
$crate::log::log_64($a as u64, 0, 0, 0, 0);
}};
($label:expr, $a:expr, $b:expr) => {{
$crate::log::log($label);
$crate::log::log_64($a as u64, $b as u64, 0, 0, 0);
}};
($label:expr, $a:expr, $b:expr, $c:expr) => {{
$crate::log::log($label);
$crate::log::log_64($a as u64, $b as u64, $c as u64, 0, 0);
}};
($label:expr, $a:expr, $b:expr, $c:expr, $d:expr) => {{
$crate::log::log($label);
$crate::log::log_64($a as u64, $b as u64, $c as u64, $d as u64, 0);
}};
($label:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {{
$crate::log::log($label);
$crate::log::log_64(
$a as u64, $b as u64, $c as u64, $d as u64, $e as u64,
);
}};
($msg:expr) => {{
$crate::log::log($msg);
}};
}
#[macro_export]
macro_rules! hopper_entrypoint {
( $process_instruction:expr ) => {
$crate::hopper_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
};
( $process_instruction:expr, $maximum:expr ) => {
#[cfg(feature = "hopper-native-backend")]
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
let mut accounts = [UNINIT; $maximum];
let (program_id, count, instruction_data) = unsafe {
$crate::__hopper_native::raw_input::deserialize_accounts::<$maximum>(
input,
&mut accounts,
)
};
let hopper_program_id = unsafe {
&*(
&program_id as *const $crate::__hopper_native::Address
as *const $crate::Address
)
};
let hopper_accounts = unsafe {
core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
};
match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
Ok(()) => $crate::__hopper_native::SUCCESS,
Err(error) => error.into(),
}
}
#[cfg(any(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"))]
$crate::__hopper_compat_entrypoint!($process_instruction, $maximum);
};
}
#[macro_export]
macro_rules! program_entrypoint {
( $process_instruction:expr ) => {
$crate::hopper_entrypoint!($process_instruction);
};
( $process_instruction:expr, $maximum:expr ) => {
$crate::hopper_entrypoint!($process_instruction, $maximum);
};
}
#[macro_export]
macro_rules! hopper_fast_entrypoint {
( $process_instruction:expr ) => {
$crate::hopper_fast_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
};
( $process_instruction:expr, $maximum:expr ) => {
#[cfg(feature = "hopper-native-backend")]
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8, ix_data: *const u8) -> u64 {
const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView> =
core::mem::MaybeUninit::<$crate::__hopper_native::AccountView>::uninit();
let mut accounts = [UNINIT; $maximum];
let ix_len = unsafe { *(ix_data.sub(8) as *const u64) as usize };
let instruction_data: &'static [u8] =
unsafe { core::slice::from_raw_parts(ix_data, ix_len) };
let program_id = unsafe {
core::ptr::read(ix_data.add(ix_len) as *const $crate::__hopper_native::Address)
};
let (program_id, count, instruction_data) = unsafe {
$crate::__hopper_native::raw_input::deserialize_accounts_fast::<$maximum>(
input,
&mut accounts,
instruction_data,
program_id,
)
};
let hopper_program_id = unsafe {
&*(
&program_id as *const $crate::__hopper_native::Address
as *const $crate::Address
)
};
let hopper_accounts = unsafe {
core::slice::from_raw_parts(accounts.as_ptr() as *const $crate::AccountView, count)
};
match $process_instruction(hopper_program_id, hopper_accounts, instruction_data) {
Ok(()) => $crate::__hopper_native::SUCCESS,
Err(error) => error.into(),
}
}
#[cfg(any(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"))]
compile_error!("hopper_fast_entrypoint! requires hopper-native-backend");
};
}
#[macro_export]
macro_rules! fast_entrypoint {
( $process_instruction:expr ) => {
$crate::hopper_fast_entrypoint!($process_instruction);
};
( $process_instruction:expr, $maximum:expr ) => {
$crate::hopper_fast_entrypoint!($process_instruction, $maximum);
};
}
#[macro_export]
macro_rules! hopper_lazy_entrypoint {
( $process:expr ) => {
#[cfg(feature = "hopper-native-backend")]
$crate::__hopper_native::hopper_lazy_entrypoint!($process);
#[cfg(any(feature = "legacy-pinocchio-compat", feature = "solana-program-backend"))]
compile_error!("hopper_lazy_entrypoint! requires hopper-native-backend");
};
}
#[macro_export]
macro_rules! lazy_entrypoint {
( $process:expr ) => {
$crate::hopper_lazy_entrypoint!($process);
};
}
#[macro_export]
macro_rules! no_allocator {
() => {
#[cfg(target_os = "solana")]
mod __hopper_allocator {
struct NoAlloc;
unsafe impl core::alloc::GlobalAlloc for NoAlloc {
unsafe fn alloc(&self, _layout: core::alloc::Layout) -> *mut u8 {
core::ptr::null_mut()
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {}
}
#[global_allocator]
static ALLOCATOR: NoAlloc = NoAlloc;
}
};
}
#[macro_export]
macro_rules! nostd_panic_handler {
() => {
#[cfg(target_os = "solana")]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
let _ = _info;
loop {
core::hint::spin_loop();
}
}
};
}