Skip to main content

hopper_runtime/compat/
native.rs

1use crate::account::AccountView;
2use crate::address::Address;
3use crate::error::ProgramError;
4use crate::ProgramResult;
5
6pub type BackendAccountView = hopper_native::AccountView;
7pub type BackendAddress = hopper_native::Address;
8pub type BackendProgramResult = hopper_native::ProgramResult;
9pub type BackendRef<'a, T> = hopper_native::borrow::Ref<'a, T>;
10pub type BackendRefMut<'a, T> = hopper_native::borrow::RefMut<'a, T>;
11pub const BACKEND_MAX_TX_ACCOUNTS: usize = hopper_native::MAX_TX_ACCOUNTS;
12pub const BACKEND_SUCCESS: u64 = hopper_native::SUCCESS;
13
14#[inline(always)]
15///
16/// # Safety
17///
18/// Caller must uphold the invariants documented for this unsafe API before invoking it.
19pub unsafe fn wrap_account_slice(accounts: &[BackendAccountView]) -> &[AccountView] {
20    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
21    unsafe { core::slice::from_raw_parts(accounts.as_ptr() as *const AccountView, accounts.len()) }
22}
23
24#[inline(always)]
25pub fn account_address(view: &BackendAccountView) -> &Address {
26    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
27    unsafe { &*(view.address() as *const BackendAddress as *const Address) }
28}
29
30#[inline(always)]
31///
32/// # Safety
33///
34/// Caller must uphold the invariants documented for this unsafe API before invoking it.
35pub unsafe fn account_owner(view: &BackendAccountView) -> &Address {
36    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
37    unsafe { &*(view.owner() as *const BackendAddress as *const Address) }
38}
39
40#[inline(always)]
41pub fn read_owner(view: &BackendAccountView) -> Address {
42    Address::from(view.read_owner())
43}
44
45#[inline(always)]
46pub fn as_backend_address(address: &Address) -> &BackendAddress {
47    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
48    unsafe { &*(address as *const Address as *const BackendAddress) }
49}
50
51#[inline(always)]
52pub fn owned_by(view: &BackendAccountView, program: &Address) -> bool {
53    view.owned_by(as_backend_address(program))
54}
55
56#[inline(always)]
57pub fn disc(view: &BackendAccountView) -> u8 {
58    view.disc()
59}
60
61#[inline(always)]
62pub fn version(view: &BackendAccountView) -> u8 {
63    view.version()
64}
65
66#[inline(always)]
67pub fn layout_id(view: &BackendAccountView) -> Option<&[u8; 8]> {
68    view.layout_id()
69}
70
71#[inline(always)]
72///
73/// # Safety
74///
75/// Caller must uphold the invariants documented for this unsafe API before invoking it.
76pub unsafe fn assign(view: &BackendAccountView, new_owner: &Address) {
77    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
78    unsafe {
79        view.assign(as_backend_address(new_owner));
80    }
81}
82
83#[inline(always)]
84pub fn close(view: &BackendAccountView) -> ProgramResult {
85    view.close().map_err(ProgramError::from)
86}
87
88#[inline(always)]
89pub fn zero_data(view: &BackendAccountView) -> ProgramResult {
90    let mut data = view.try_borrow_mut().map_err(ProgramError::from)?;
91    let mut i = 0;
92    while i < data.len() {
93        data[i] = 0;
94        i += 1;
95    }
96    Ok(())
97}
98
99#[cfg(target_os = "solana")]
100#[inline(always)]
101pub fn find_program_address(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
102    let (address, bump) =
103        hopper_native::pda::find_program_address(seeds, as_backend_address(program_id));
104    (Address::from(address), bump)
105}
106
107#[inline(always)]
108pub fn create_program_address(
109    seeds: &[&[u8]],
110    program_id: &Address,
111) -> Result<Address, ProgramError> {
112    #[cfg(target_os = "solana")]
113    {
114        hopper_native::pda::create_program_address(seeds, as_backend_address(program_id))
115            .map(Address::from)
116            .map_err(|_| ProgramError::InvalidSeeds)
117    }
118    #[cfg(not(target_os = "solana"))]
119    {
120        let _ = (seeds, program_id);
121        Err(ProgramError::InvalidSeeds)
122    }
123}
124
125#[inline(always)]
126///
127/// # Safety
128///
129/// Caller must uphold the invariants documented for this unsafe API before invoking it.
130pub unsafe fn process_entrypoint<const MAX: usize>(
131    input: *mut u8,
132    process_instruction: fn(&BackendAddress, &[BackendAccountView], &[u8]) -> BackendProgramResult,
133) -> u64 {
134    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
135    unsafe { hopper_native::entrypoint::process_entrypoint::<MAX>(input, process_instruction) }
136}
137
138#[inline(always)]
139pub fn bridge_to_runtime(
140    program_id: &BackendAddress,
141    accounts: &[BackendAccountView],
142    data: &[u8],
143    process_instruction: fn(&Address, &[AccountView], &[u8]) -> ProgramResult,
144) -> BackendProgramResult {
145    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
146    let hopper_id = unsafe { &*(program_id as *const BackendAddress as *const Address) };
147    let hopper_accounts = unsafe { wrap_account_slice(accounts) };
148    match process_instruction(hopper_id, hopper_accounts, data) {
149        Ok(()) => Ok(()),
150        Err(error) => Err(error.into()),
151    }
152}
153
154#[inline(always)]
155pub fn set_return_data(data: &[u8]) {
156    #[cfg(target_os = "solana")]
157    // SAFETY: This block is part of Hopper's audited zero-copy/backend boundary; surrounding checks and caller contracts uphold the required raw-pointer, layout, and aliasing invariants.
158    unsafe {
159        hopper_native::syscalls::sol_set_return_data(data.as_ptr(), data.len() as u64);
160    }
161    #[cfg(not(target_os = "solana"))]
162    {
163        let _ = data;
164    }
165}
166
167impl From<BackendAddress> for Address {
168    #[inline(always)]
169    fn from(address: BackendAddress) -> Self {
170        Self(address.to_bytes())
171    }
172}
173
174impl From<Address> for BackendAddress {
175    #[inline(always)]
176    fn from(address: Address) -> Self {
177        BackendAddress::new_from_array(address.to_bytes())
178    }
179}