#![no_std]
#![deny(unsafe_op_in_unsafe_fn)]
#![cfg_attr(feature = "copy", allow(clippy::clone_on_copy))]
#[cfg(any(test, feature = "thread-local-registry"))]
extern crate std;
#[doc(hidden)]
pub mod native_boundary;
pub mod account;
pub mod account_wrappers;
pub mod address;
pub mod audit;
pub mod behavior;
pub mod borrow;
pub(crate) mod borrow_registry;
pub mod compact;
#[cfg(any(not(target_os = "solana"), feature = "remaining-compute-units-syscall"))]
pub mod compute;
pub mod cpi;
pub mod cpi_event;
pub mod crank;
pub mod crypto;
pub mod dyn_cpi;
pub mod error;
pub mod field_map;
pub mod foreign;
pub mod interop;
pub mod lamports;
pub mod lazy;
pub mod log;
pub mod memory;
pub mod migrate;
pub mod pod;
pub mod policy;
pub mod proof;
pub mod ref_only;
pub mod result;
pub mod segment;
pub mod tail;
pub mod utils;
pub mod zerocopy;
#[doc(hidden)]
pub use zerocopy::__sealed;
pub mod context;
pub mod instruction;
pub mod layout;
pub mod option_byte;
pub mod pda;
pub mod remaining;
pub mod rent;
pub mod return_data;
pub mod segment_borrow;
pub mod segment_lease;
pub mod syscall;
pub mod syscalls;
pub mod system;
pub mod token;
pub mod token_2022_ext;
pub mod token_mint;
pub mod write_policy;
pub use account::AccountView;
pub use account_wrappers::{
Account, InitAccount, Interface, InterfaceAccount, InterfaceAccountLayout,
InterfaceAccountResolve, InterfaceSpec, Program, ProgramId, Signer as HopperSigner,
SystemAccount, SystemId, UncheckedAccount,
};
pub use address::Address;
pub use audit::{AccountAudit, DuplicateAccount};
pub use behavior::{BehaviorChecked, BehaviorWrite, HopperBehavior};
pub use borrow::{Ref, RefMut};
pub use compact::{CompactDynamicLayout, CompactLayout, COMPACT_BODY_OFFSET};
#[cfg(any(not(target_os = "solana"), feature = "remaining-compute-units-syscall"))]
pub use compute::{check_compute_units, remaining_compute_units, require_compute_units};
pub use context::{Context, ScopedContext};
pub use cpi::{invoke, invoke_checked, invoke_signed, invoke_signed_checked};
#[cfg(feature = "crypto-big-mod-exp")]
pub use crypto::big_mod_exp;
#[cfg(feature = "crypto-bn254")]
pub use crypto::{
alt_bn128_add, alt_bn128_g1_addition_be, alt_bn128_g1_compress_be, alt_bn128_g1_decompress_be,
alt_bn128_g1_multiplication_be, alt_bn128_g2_compress_be, alt_bn128_g2_decompress_be,
alt_bn128_mul, alt_bn128_pairing, alt_bn128_pairing_be,
};
pub use crypto::{
blake3, blake3_single, keccak256, keccak256_single, recover_ethereum_address,
secp256k1_recover, sha256, sha256_single,
};
#[cfg(feature = "crypto-curve")]
pub use crypto::{curve_group_add, curve_group_mul, curve_group_sub, curve_multiscalar_mul};
#[cfg(feature = "crypto-poseidon")]
pub use crypto::{poseidon_bn254_x5, poseidon_hash, poseidon_hashv};
pub use error::ProgramError;
pub use field_map::{FieldInfo, FieldMap};
pub use foreign::{
ExplainExternal, ExternalAccount, ExternalBytes, ExternalChecked, ExternalExplainSink,
ExternalLens, ExternalLensValue, ExternalProof, ExternalResolve, ExternalZeroCopy, ForeignLens,
ForeignManifest,
};
pub use interop::TransparentAddress;
pub use lamports::transfer_lamports;
pub use lazy::LazyContext;
pub use migrate::{
apply_pending_migrations, ensure_fits_with_rent, migrate_layout, migrate_layout_resizing,
validate_header_for_epoch_migration, LayoutMigration, MigrationEdge,
};
pub use policy::{HopperInstructionPolicy, HopperProgramPolicy, HopperProgramProfile};
pub use proof::{
AccountProof, ExecutableChecked, HasOneChecked, LayoutChecked, OwnerChecked, SeedsChecked,
SignerChecked, TokenExtensionsChecked, Unchecked, WritableChecked,
};
pub use ref_only::HopperRefOnly;
pub use remaining::{
RemainingAccountViews, RemainingAccounts, RemainingError, RemainingExternalAccounts,
RemainingGroup, RemainingLazy, RemainingLazySlot, RemainingMode, RemainingSigners,
RemainingTyped, MAX_REMAINING_ACCOUNTS,
};
pub use return_data::{get_return_data, set_return_data, try_set_return_data, ReturnData};
pub use tail::{
borrow_address_slice, borrow_bounded_str, read_tail, read_tail_len, seq_capacity_for,
seq_region_bytes_for, tail_capacity, tail_payload, write_tail, write_tail_payload,
BoundedString, BoundedVec, HopperString, HopperVec, SeqElement, SeqTailRead, SeqTailWrite,
TailBytes, TailCodec, TailElement, TailSeq, TailSeqIter, TailSeqMut, TailStr, SEQ_LEN_PREFIX,
};
#[macro_export]
macro_rules! layout_migrations {
( $layout:ty = [ $( $edge:expr ),+ $(,)? ] $(,)? ) => {
impl $crate::migrate::LayoutMigration for $layout {
const MIGRATIONS: &'static [$crate::migrate::MigrationEdge] = &[
$( $edge ),+
];
}
};
}
pub use instruction::CpiAccount;
pub use instruction::{
InstructionAccount, InstructionView, Seed, Signer, StoredAccountMeta, StoredInstruction,
};
pub use layout::{HopperHeader, LayoutContract, LayoutInfo};
pub use pod::{read_unaligned_value, Pod, ValuePod, Zeroable};
pub use result::ProgramResult;
pub use segment::{
FieldCapability, Segment, TypedSegment, FIELD_POLICY_AUTHORITY_GATED,
FIELD_POLICY_CHECKED_MATH, FIELD_POLICY_IMMUTABLE_AFTER_INIT, FIELD_ROLE_AUTHORITY,
FIELD_ROLE_BALANCE, FIELD_ROLE_DATA, FIELD_ROLE_VERSION,
};
pub use segment_borrow::{AccessKind, SegmentBorrow, SegmentBorrowGuard, SegmentBorrowRegistry};
pub use segment_lease::{SegRef, SegRefMut, SegmentLease, SegmentsMut};
pub use write_policy::{
ParametricWriteRange, WritePolicy, WriteRange, WRITE_POLICY_VIOLATION_PAGE,
};
pub use zerocopy::{AccountLayout, WireLayout, ZeroCopy};
pub const MAX_TX_ACCOUNTS: usize = native_boundary::BACKEND_MAX_TX_ACCOUNTS;
pub const SUCCESS: u64 = native_boundary::BACKEND_SUCCESS;
#[doc(hidden)]
pub use hopper_native as __hopper_native;
pub use hopper_native::sha256;
#[doc(hidden)]
pub use hopper_native::address::decode_base58_32 as __decode_base58_32;
#[macro_export]
macro_rules! address {
( $literal:expr ) => {
$crate::Address::new_from_array($crate::__decode_base58_32($literal))
};
}
#[macro_export]
macro_rules! declare_id {
( $literal:expr ) => {
pub const ID: $crate::Address = $crate::address!($literal);
#[inline]
pub const fn id() -> $crate::Address {
ID
}
#[inline]
pub fn check_id(other: &$crate::Address) -> bool {
*other == ID
}
};
}
#[macro_export]
macro_rules! const_pda {
( $program_id:expr, [ $( $seed:expr ),* $(,)? ], $bump:expr ) => {
$crate::pda::const_program_address(&$program_id, &[ $( $seed as &[u8] ),* ], $bump)
};
}
#[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 !$crate::address::keys_eq(
::core::convert::AsRef::<[u8; 32]>::as_ref(&$left),
::core::convert::AsRef::<[u8; 32]>::as_ref(&$right),
) {
return Err($err);
}
};
( $left:expr, $right:expr ) => {
if !$crate::address::keys_eq(
::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 $crate::address::keys_eq(
::core::convert::AsRef::<[u8; 32]>::as_ref(&$left),
::core::convert::AsRef::<[u8; 32]>::as_ref(&$right),
) {
return Err($err);
}
};
( $left:expr, $right:expr ) => {
if $crate::address::keys_eq(
::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 = $crate::cpi_event::CpiEvent::tag(&__ev);
let __payload: &[u8] = $crate::cpi_event::CpiEvent::payload_bytes(&__ev);
let mut __buf = [0u8; 2 + 1 + $crate::cpi_event::MAX_EVENT_PAYLOAD];
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] = [$crate::cpi_event::EVENT_AUTHORITY_SEED, &__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 ) => {
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
const UNINIT: core::mem::MaybeUninit<$crate::__hopper_native::AccountView<'static>> =
core::mem::MaybeUninit::<$crate::__hopper_native::AccountView<'static>>::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(),
}
}
};
}
#[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);
};
}
#[cfg(feature = "simd-0321")]
#[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 ) => {
#[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 (program_id, count, instruction_data) = if ix_data.is_null() {
unsafe {
$crate::__hopper_native::raw_input::deserialize_accounts::<$maximum>(
input,
&mut accounts,
)
}
} else {
let ix_len =
unsafe { core::ptr::read_unaligned(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: &'static $crate::__hopper_native::Address =
unsafe { &*(ix_data.add(ix_len) as *const $crate::__hopper_native::Address) };
if $crate::__hopper_native::raw_input::SIMD_0449_TABLE_ENABLED {
unsafe {
$crate::__hopper_native::raw_input::deserialize_accounts_0449_into::<$maximum>(
input,
&mut accounts,
instruction_data,
program_id,
)
}
} else {
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(not(feature = "simd-0321"))]
#[macro_export]
macro_rules! hopper_fast_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_exact_entrypoint {
( $( ( $disc:literal, $bound:expr, $helper:path ) ),* $(,)? ) => {
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8, ix_data: *const u8) -> u64 {
if ix_data.is_null() {
return $crate::ProgramError::InvalidArgument.into();
}
let ix_len =
unsafe { core::ptr::read_unaligned(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: &'static $crate::Address =
unsafe { &*(ix_data.add(ix_len) as *const $crate::Address) };
let bound: usize = match instruction_data.first() {
$( ::core::option::Option::Some(&$disc) => $bound, )*
_ => return $crate::ProgramError::InvalidInstructionData.into(),
};
const WIDEST: usize = $crate::max_account_bound(&[ $( $bound ),* ]);
const UNINIT: core::mem::MaybeUninit<
$crate::__hopper_native::AccountView<'static>,
> = core::mem::MaybeUninit::uninit();
let mut views = [UNINIT; WIDEST];
let count = unsafe {
$crate::__hopper_native::raw_input::deserialize_leading_accounts::<WIDEST>(
input,
&mut views,
bound,
)
};
let accounts = unsafe {
core::slice::from_raw_parts(views.as_ptr() as *const $crate::AccountView<'_>, count)
};
let mut ctx = $crate::Context::new(program_id, accounts, instruction_data);
let result: ::core::result::Result<(), $crate::ProgramError> =
match instruction_data.first() {
$( ::core::option::Option::Some(&$disc) => $helper(&mut ctx, instruction_data), )*
_ => ::core::result::Result::Err($crate::ProgramError::InvalidInstructionData),
};
match result {
::core::result::Result::Ok(()) => $crate::__hopper_native::SUCCESS,
::core::result::Result::Err(error) => error.into(),
}
}
};
}
#[doc(hidden)]
pub const fn max_account_bound(bounds: &[usize]) -> usize {
let mut widest = 0usize;
let mut i = 0usize;
while i < bounds.len() {
if bounds[i] > widest {
widest = bounds[i];
}
i += 1;
}
widest
}
#[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 ) => {
$crate::__hopper_native::hopper_lazy_entrypoint!(
|__hopper_native_ctx: &mut $crate::__hopper_native::LazyContext|
-> ::core::result::Result<(), $crate::__hopper_native::error::ProgramError> {
let mut __hopper_ctx =
$crate::lazy::LazyContext::from_native(__hopper_native_ctx);
match $process(&mut __hopper_ctx) {
::core::result::Result::Ok(()) => ::core::result::Result::Ok(()),
::core::result::Result::Err(e) => {
::core::result::Result::Err(::core::convert::From::from(e))
}
}
}
);
};
}
#[macro_export]
macro_rules! lazy_entrypoint {
( $process:expr ) => {
$crate::hopper_lazy_entrypoint!($process);
};
}
#[macro_export]
macro_rules! no_allocator {
() => {
$crate::__hopper_native::no_allocator!();
};
}
#[macro_export]
macro_rules! default_allocator {
() => {
#[cfg(target_os = "solana")]
#[global_allocator]
static ALLOCATOR: $crate::__hopper_native::BumpAllocator =
$crate::__hopper_native::BumpAllocator {
start: $crate::__hopper_native::HEAP_START_ADDRESS,
len: $crate::__hopper_native::HEAP_LENGTH,
};
};
}
#[macro_export]
macro_rules! nostd_panic_handler {
() => {
$crate::__hopper_native::nostd_panic_handler!();
};
}