hopper_runtime/
native_boundary.rs1use crate::account::AccountView;
2use crate::address::Address;
3use crate::error::ProgramError;
4use crate::ProgramResult;
5
6pub type BackendAccountView<'info> = hopper_native::AccountView<'info>;
7pub type BackendAccountSlice<'info> = &'info [BackendAccountView<'info>];
8pub type BackendAddress = hopper_native::Address;
9pub type BackendProgramResult = hopper_native::ProgramResult;
10pub type BackendRef<'a, T> = hopper_native::borrow::Ref<'a, T>;
11pub type BackendRefMut<'a, T> = hopper_native::borrow::RefMut<'a, T>;
12pub const BACKEND_MAX_TX_ACCOUNTS: usize = hopper_native::MAX_TX_ACCOUNTS;
13pub const BACKEND_SUCCESS: u64 = hopper_native::SUCCESS;
14
15#[inline(always)]
21pub unsafe fn wrap_account_slice<'info>(
22 accounts: &'info [BackendAccountView<'info>],
23) -> &'info [AccountView<'info>] {
24 unsafe {
27 core::slice::from_raw_parts(
28 accounts.as_ptr() as *const AccountView<'info>,
29 accounts.len(),
30 )
31 }
32}
33
34#[inline(always)]
35pub fn account_address<'a>(view: &'a BackendAccountView<'a>) -> &'a Address {
36 unsafe { &*(view.address() as *const BackendAddress as *const Address) }
39}
40
41#[inline(always)]
46pub unsafe fn account_owner<'a>(view: &'a BackendAccountView<'a>) -> &'a Address {
47 unsafe { &*(view.owner() as *const BackendAddress as *const Address) }
50}
51
52#[inline(always)]
53pub fn read_owner(view: &BackendAccountView<'_>) -> Address {
54 Address::from(view.read_owner())
55}
56
57#[inline(always)]
58pub fn as_backend_address(address: &Address) -> &BackendAddress {
59 unsafe { &*(address as *const Address as *const BackendAddress) }
61}
62
63#[inline(always)]
64pub fn owned_by(view: &BackendAccountView<'_>, program: &Address) -> bool {
65 view.owned_by(as_backend_address(program))
66}
67
68#[inline(always)]
69pub fn disc(view: &BackendAccountView<'_>) -> u8 {
70 view.disc()
71}
72
73#[inline(always)]
74pub fn version(view: &BackendAccountView<'_>) -> u8 {
75 view.version()
76}
77
78#[inline(always)]
79pub fn layout_id<'a>(view: &'a BackendAccountView<'a>) -> Option<&'a [u8; 8]> {
80 view.layout_id()
81}
82
83#[inline(always)]
88pub unsafe fn assign(view: &BackendAccountView<'_>, new_owner: &Address) {
89 unsafe {
92 view.assign(as_backend_address(new_owner));
93 }
94}
95
96#[inline(always)]
110pub fn try_set_lamports(view: &BackendAccountView<'_>, lamports: u64) -> ProgramResult {
111 crate::write_policy::check_lamport_mutation(account_address(view))?;
112 view.set_lamports(lamports);
113 Ok(())
114}
115
116#[inline(always)]
121pub fn close(view: &BackendAccountView<'_>) -> ProgramResult {
122 crate::write_policy::check_lamport_mutation(account_address(view))?;
123 view.close().map_err(ProgramError::from)
124}
125
126#[inline(always)]
127pub fn zero_data(view: &BackendAccountView<'_>) -> ProgramResult {
128 let mut data = view.try_borrow_mut().map_err(ProgramError::from)?;
129 let mut i = 0;
130 while i < data.len() {
131 data[i] = 0;
132 i += 1;
133 }
134 Ok(())
135}
136
137#[inline(always)]
138pub fn resize(view: &BackendAccountView<'_>, new_len: usize) -> ProgramResult {
139 view.resize(new_len).map_err(ProgramError::from)
140}
141
142#[inline(always)]
143pub fn resize_raw(view: &BackendAccountView<'_>, new_len: usize) -> ProgramResult {
144 view.resize_raw(new_len).map_err(ProgramError::from)
145}
146
147#[cfg(target_os = "solana")]
148#[inline(always)]
149pub fn find_program_address(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
150 let (address, bump) =
151 hopper_native::pda::find_program_address(seeds, as_backend_address(program_id));
152 (Address::from(address), bump)
153}
154
155#[inline(always)]
156pub fn create_program_address(
157 seeds: &[&[u8]],
158 program_id: &Address,
159) -> Result<Address, ProgramError> {
160 #[cfg(target_os = "solana")]
161 {
162 hopper_native::pda::create_program_address(seeds, as_backend_address(program_id))
163 .map(Address::from)
164 .map_err(|_| ProgramError::InvalidSeeds)
165 }
166 #[cfg(not(target_os = "solana"))]
167 {
168 let _ = (seeds, program_id);
169 Err(ProgramError::InvalidSeeds)
170 }
171}
172
173#[inline(always)]
178pub unsafe fn process_entrypoint<const MAX: usize>(
179 input: *mut u8,
180 process_instruction: fn(
181 &BackendAddress,
182 BackendAccountSlice<'_>,
183 &[u8],
184 ) -> BackendProgramResult,
185) -> u64 {
186 unsafe { hopper_native::entrypoint::process_entrypoint::<MAX>(input, process_instruction) }
189}
190
191#[inline(always)]
192pub fn bridge_to_runtime(
193 program_id: &BackendAddress,
194 accounts: BackendAccountSlice<'_>,
195 data: &[u8],
196 process_instruction: for<'info> fn(
197 &'info Address,
198 &'info [AccountView<'info>],
199 &'info [u8],
200 ) -> ProgramResult,
201) -> BackendProgramResult {
202 let hopper_id = unsafe { &*(program_id as *const BackendAddress as *const Address) };
205 let hopper_accounts = unsafe { wrap_account_slice(accounts) };
208 match process_instruction(hopper_id, hopper_accounts, data) {
209 Ok(()) => Ok(()),
210 Err(error) => Err(error.into()),
211 }
212}
213
214impl From<BackendAddress> for Address {
215 #[inline(always)]
216 fn from(address: BackendAddress) -> Self {
217 Self(address.to_bytes())
218 }
219}
220
221impl From<Address> for BackendAddress {
222 #[inline(always)]
223 fn from(address: Address) -> Self {
224 BackendAddress::new_from_array(address.to_bytes())
225 }
226}
227
228#[doc(hidden)]
229#[macro_export]
230macro_rules! __hopper_native_entrypoint {
231 ( $process_instruction:expr, $maximum:expr ) => {
232 #[no_mangle]
236 pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
237 #[inline(always)]
238 fn __hopper_bridge(
239 program_id: &$crate::native_boundary::BackendAddress,
240 accounts: $crate::native_boundary::BackendAccountSlice<'_>,
241 data: &[u8],
242 ) -> $crate::native_boundary::BackendProgramResult {
243 $crate::native_boundary::bridge_to_runtime(
244 program_id,
245 accounts,
246 data,
247 $process_instruction,
248 )
249 }
250 unsafe {
253 $crate::native_boundary::process_entrypoint::<$maximum>(input, __hopper_bridge)
254 }
255 }
256 };
257}