1#![no_std]
2
3use {bytemuck::NoUninit, paste::paste, pinocchio::program_error::ProgramError};
4
5mod args;
6mod array;
7mod iterator;
8mod program_id;
9mod remaining_accounts;
10
11pub use {args::*, array::*, iterator::*, program_id::*, remaining_accounts::*};
12use {
13 pinocchio::{account_info::AccountInfo, cpi::set_return_data, pubkey::Pubkey},
14 typhoon_errors::Error,
15};
16
17pub trait Context {}
19
20pub trait HandlerContext<'a, 'b, 'c>: Sized {
21 fn from_entrypoint(
22 program_id: &'a Pubkey,
23 accounts: &mut &'b [AccountInfo],
24 instruction_data: &mut &'c [u8],
25 ) -> Result<Self, Error>;
26}
27
28pub trait Handler<'a, 'b, 'c, T> {
29 type Output: NoUninit;
30
31 fn call(
32 self,
33 program_id: &'a Pubkey,
34 accounts: &mut &'b [AccountInfo],
35 instruction_data: &mut &'c [u8],
36 ) -> Result<Self::Output, Error>;
37}
38
39impl<F, O> Handler<'_, '_, '_, ()> for F
40where
41 F: FnOnce() -> Result<O, Error>,
42 O: NoUninit,
43{
44 type Output = O;
45
46 fn call(
47 self,
48 _program_id: &Pubkey,
49 _accounts: &mut &[AccountInfo],
50 _instruction_data: &mut &[u8],
51 ) -> Result<Self::Output, Error> {
52 (self)()
53 }
54}
55
56macro_rules! impl_handler {
57 ($( $t:ident ),+) => {
58 impl<'a, 'b, 'c, $( $t, )* F, O> Handler<'a, 'b, 'c, ($( $t, )*)> for F
59 where
60 F: FnOnce($( $t ),*) -> Result<O, Error>,
61 O: NoUninit,
62 $(
63 $t: HandlerContext<'a, 'b, 'c>,
64 )*
65 {
66 type Output = O;
67
68 fn call(
69 self,
70 program_id: &'a Pubkey,
71 accounts: &mut &'b [AccountInfo],
72 instruction_data: &mut &'c [u8],
73 ) -> Result<Self::Output, Error> {
74 paste! {
75 $(
76 let [<$t:lower>] = $t::from_entrypoint(program_id, accounts, instruction_data)?;
77 )*
78 (self)($( [<$t:lower>], )*)
79 }
80 }
81 }
82 };
83}
84
85impl_handler!(T1);
86impl_handler!(T1, T2);
87impl_handler!(T1, T2, T3);
88impl_handler!(T1, T2, T3, T4);
89impl_handler!(T1, T2, T3, T4, T5);
90impl_handler!(T1, T2, T3, T4, T5, T6);
91impl_handler!(T1, T2, T3, T4, T5, T6, T7);
92
93pub fn handle<'a, 'b, 'c, T, H>(
94 program_id: &'a Pubkey,
95 mut accounts: &'b [AccountInfo],
96 mut instruction_data: &'c [u8],
97 handler: H,
98) -> Result<(), Error>
99where
100 H: Handler<'a, 'b, 'c, T>,
101{
102 match handler.call(program_id, &mut accounts, &mut instruction_data) {
103 Ok(res) => {
104 if core::mem::size_of::<H::Output>() > 0 {
105 set_return_data(bytemuck::bytes_of(&res));
106 }
107
108 Ok(())
109 }
110 Err(err) => Err(err),
111 }
112}
113
114#[macro_export]
115macro_rules! basic_router {
116 ($($dis:literal => $fn_ident: ident),+ $(,)?) => {
117 |program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]| {
118 let (discriminator, data) = instruction_data
119 .split_first()
120 .ok_or(ProgramError::InvalidInstructionData)?;
121
122 let result = match discriminator {
123 $($dis => handle(program_id, accounts, data, $fn_ident),)*
124 _ => Err(ErrorCode::UnknownInstruction.into()),
125 };
126
127 #[cfg(feature = "logging")]
128 result.inspect_err(log_error)?;
129
130 #[cfg(not(feature = "logging"))]
131 result?;
132
133 Ok(())
134 }
135 };
136}
137
138pub type EntryFn = fn(&Pubkey, &[AccountInfo], &[u8]) -> Result<(), ProgramError>;
139
140#[macro_export]
141macro_rules! entrypoint {
142 () => {
143 $crate::entrypoint!(@inner inline(always));
144 };
145 (no_inline) => {
146 $crate::entrypoint!(@inner inline(never));
147 };
148 (@inner $($inline:tt)*) => {
149 program_entrypoint!(process_instruction);
150
151 #[ $($inline)* ]
152 pub fn process_instruction(
153 program_id: &Pubkey,
154 accounts: &[AccountInfo],
155 instruction_data: &[u8],
156 ) -> Result<(), ProgramError> {
157 ROUTER(program_id, accounts, instruction_data)
158 }
159 };
160}