hopper_runtime/interop.rs
1//! Cross-framework type interop for Hopper.
2//!
3//! Hopper keeps its own `Address` and `AccountView` types because they
4//! carry segment metadata, layout fingerprints, and borrow-tracking that
5//! external types lack. This module provides `From`/`Into` conversions
6//! so Hopper code can interoperate with the wider Solana ecosystem
7//! without loss of type safety.
8//!
9//! # Zero-cost reference casts
10//!
11//! Both Hopper's `Address` and the upstream types (`pinocchio::Address`,
12//! `solana_program::pubkey::Pubkey`) are `#[repr(transparent)]` over
13//! `[u8; 32]`. This means reference casts are valid and zero-cost:
14//!
15//! ```ignore
16//! let hopper_addr: &Address = Address::from_ref(upstream_addr);
17//! let upstream_ref: &[u8; 32] = hopper_addr.as_array();
18//! ```
19//!
20//! # By-value conversions
21//!
22//! `From`/`Into` impls are provided automatically via the active backend.
23//! When `legacy-pinocchio-compat` is enabled, `From<pinocchio::Address>` and
24//! `From<Address> for pinocchio::Address` are available. When
25//! `solana-program-backend` is enabled, the same exists for `Pubkey`.
26//!
27//! # Backend-agnostic conversions
28//!
29//! Regardless of backend, Hopper `Address` always converts to/from
30//! `[u8; 32]`, making it trivially interoperable with any type that
31//! also wraps 32 bytes.
32
33use crate::address::Address;
34
35// ── Zero-cost reference conversions ──────────────────────────────────
36
37impl Address {
38 /// Zero-cost borrow as a reference to any `#[repr(transparent)]`
39 /// 32-byte type that shares layout with `[u8; 32]`.
40 ///
41 /// This is the preferred way to pass a Hopper `Address` where an
42 /// upstream reference is expected (e.g. `&pinocchio::Address` or
43 /// `&Pubkey`).
44 ///
45 /// # Safety
46 ///
47 /// Safe because `Address` is `#[repr(transparent)]` over `[u8; 32]`
48 /// and any upstream 32-byte address type shares this layout.
49 #[inline(always)]
50 pub fn as_upstream<T>(&self) -> &T
51 where
52 T: TransparentAddress,
53 {
54 // SAFETY: Both types are #[repr(transparent)] over [u8; 32].
55 unsafe { &*(self as *const Address as *const T) }
56 }
57
58 /// Construct a Hopper `Address` reference from any `#[repr(transparent)]`
59 /// 32-byte address type.
60 #[inline(always)]
61 pub fn from_upstream<T>(upstream: &T) -> &Address
62 where
63 T: TransparentAddress,
64 {
65 // SAFETY: Both types are #[repr(transparent)] over [u8; 32].
66 unsafe { &*(upstream as *const T as *const Address) }
67 }
68}
69
70/// Marker trait for types that are `#[repr(transparent)]` over `[u8; 32]`.
71///
72/// # Safety
73///
74/// Implementors must be `#[repr(transparent)]` wrappers around `[u8; 32]`
75/// with no additional invariants. This enables zero-cost reference casts.
76pub unsafe trait TransparentAddress: Sized {}
77
78// Hopper's own Address is trivially transparent.
79unsafe impl TransparentAddress for Address {}
80
81#[cfg(feature = "legacy-pinocchio-compat")]
82unsafe impl TransparentAddress for pinocchio::address::Address {}
83
84#[cfg(feature = "solana-program-backend")]
85unsafe impl TransparentAddress for ::solana_program::pubkey::Pubkey {}
86
87// ── By-value conversions (re-documented for discoverability) ─────────
88//
89// The actual From/Into impls live in the compat modules (compat/pinocchio.rs,
90// compat/solana_program.rs) where backend types are in scope. This module
91// just makes them discoverable and documents the interop story.
92//
93// Available conversions by backend:
94//
95// legacy-pinocchio-compat:
96// From<pinocchio::Address> for Address
97// From<Address> for pinocchio::Address
98//
99// solana-program-backend:
100// From<solana_program::Pubkey> for Address
101// From<Address> for solana_program::Pubkey
102//
103// hopper-native-backend:
104// From<hopper_native::Address> for Address (via [u8; 32])
105// From<Address> for hopper_native::Address (via [u8; 32])
106
107// ── hopper-native backend conversions ────────────────────────────────
108//
109// From/Into impls for hopper_native::Address <-> Address already live
110// in compat/native.rs. We only add the TransparentAddress marker here.
111
112#[cfg(feature = "hopper-native-backend")]
113unsafe impl TransparentAddress for hopper_native::address::Address {}