#![no_std]
mod arg;
mod array;
mod iterator;
mod program_id;
mod remaining_accounts;
pub use {arg::*, array::*, iterator::*, program_id::*, remaining_accounts::*};
use {
bytemuck::NoUninit, pastey::paste, solana_account_view::AccountView, solana_address::Address,
solana_instruction_view::cpi::set_return_data, solana_program_error::ProgramError,
typhoon_errors::Error,
};
pub trait Context {}
pub trait HandlerContext<'a, 'b, 'c>: Sized {
fn from_entrypoint(
program_id: &'a Address,
accounts: &mut &'b [AccountView],
instruction_data: &mut &'c [u8],
) -> Result<Self, Error>;
}
pub trait Handler<'a, 'b, 'c, T> {
type Output: NoUninit;
fn call(
self,
program_id: &'a Address,
accounts: &mut &'b [AccountView],
instruction_data: &mut &'c [u8],
) -> Result<Self::Output, Error>;
}
impl<F, O> Handler<'_, '_, '_, ()> for F
where
F: FnOnce() -> Result<O, Error>,
O: NoUninit,
{
type Output = O;
fn call(
self,
_program_id: &Address,
_accounts: &mut &[AccountView],
_instruction_data: &mut &[u8],
) -> Result<Self::Output, Error> {
(self)()
}
}
macro_rules! impl_handler {
($( $t:ident ),+) => {
impl<'a, 'b, 'c, $( $t, )* F, O> Handler<'a, 'b, 'c, ($( $t, )*)> for F
where
F: FnOnce($( $t ),*) -> Result<O, Error>,
O: NoUninit,
$(
$t: HandlerContext<'a, 'b, 'c>,
)*
{
type Output = O;
fn call(
self,
program_id: &'a Address,
accounts: &mut &'b [AccountView],
instruction_data: &mut &'c [u8],
) -> Result<Self::Output, Error> {
paste! {
$(
let [<$t:lower>] = $t::from_entrypoint(program_id, accounts, instruction_data)?;
)*
(self)($( [<$t:lower>], )*)
}
}
}
};
}
impl_handler!(T1);
impl_handler!(T1, T2);
impl_handler!(T1, T2, T3);
impl_handler!(T1, T2, T3, T4);
impl_handler!(T1, T2, T3, T4, T5);
impl_handler!(T1, T2, T3, T4, T5, T6);
impl_handler!(T1, T2, T3, T4, T5, T6, T7);
pub fn handle<'a, 'b, 'c, T, H>(
program_id: &'a Address,
mut accounts: &'b [AccountView],
mut instruction_data: &'c [u8],
handler: H,
) -> Result<(), Error>
where
H: Handler<'a, 'b, 'c, T>,
{
match handler.call(program_id, &mut accounts, &mut instruction_data) {
Ok(res) => {
if core::mem::size_of::<H::Output>() > 0 {
set_return_data(bytemuck::bytes_of(&res));
}
Ok(())
}
Err(err) => Err(err),
}
}
#[macro_export]
macro_rules! basic_router {
($($dis:literal => $fn_ident: ident),+ $(,)?) => {
|program_id: &Address, accounts: &[AccountView], instruction_data: &[u8]| {
let (discriminator, data) = instruction_data
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
let result = match discriminator {
$($dis => handle(program_id, accounts, data, $fn_ident),)*
_ => Err(ErrorCode::UnknownInstruction.into()),
};
#[cfg(feature = "logging")]
result.inspect_err(|e| log_error::<LogError>(e))?;
#[cfg(not(feature = "logging"))]
result?;
Ok(())
}
};
}
pub type EntryFn = fn(&Address, &[AccountView], &[u8]) -> Result<(), ProgramError>;
#[macro_export]
macro_rules! entrypoint {
() => {
$crate::entrypoint!(@inner inline(always));
};
(no_inline) => {
$crate::entrypoint!(@inner inline(never));
};
(@inner $($inline:tt)*) => {
program_entrypoint!(process_instruction);
#[ $($inline)* ]
pub fn process_instruction(
program_id: &Address,
accounts: &[AccountView],
instruction_data: &[u8],
) -> Result<(), ProgramError> {
ROUTER(program_id, accounts, instruction_data)
}
};
}