Skip to main content

hopper_native/
system.rs

1//! System program CPI instructions.
2//!
3//! Provides CreateAccount, Transfer, Assign, and Allocate builders
4//! that invoke the system program via `sol_invoke_signed_c`.
5
6use crate::account_view::AccountView;
7use crate::address::Address;
8use crate::instruction::{CpiAccount, Signer};
9use crate::ProgramResult;
10
11/// System program address: 11111111111111111111111111111111
12pub const SYSTEM_PROGRAM_ID: Address = Address::new_from_array([
13    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14]);
15
16// ── CreateAccount ────────────────────────────────────────────────────
17
18/// Builder for the system program's CreateAccount instruction.
19pub struct CreateAccount<'a, 'b> {
20    pub from: &'a AccountView,
21    pub to: &'a AccountView,
22    pub lamports: u64,
23    pub space: u64,
24    pub owner: &'b Address,
25}
26
27impl CreateAccount<'_, '_> {
28    /// Invoke the CreateAccount instruction (no PDA signers).
29    #[inline]
30    pub fn invoke(&self) -> ProgramResult {
31        self.invoke_signed(&[])
32    }
33
34    /// Invoke the CreateAccount instruction with PDA signers.
35    #[inline]
36    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
37        // Instruction data: u32(0) + u64(lamports) + u64(space) + [u8;32](owner)
38        let mut data = [0u8; 52];
39        // index 0 = CreateAccount (already zero)
40        data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
41        data[12..20].copy_from_slice(&self.space.to_le_bytes());
42        data[20..52].copy_from_slice(self.owner.as_array());
43
44        let accounts = [CpiAccount::from(self.from), CpiAccount::from(self.to)];
45
46        invoke_system(&data, &accounts, signers)
47    }
48}
49
50// ── Transfer ─────────────────────────────────────────────────────────
51
52/// Builder for the system program's Transfer instruction.
53pub struct Transfer<'a> {
54    pub from: &'a AccountView,
55    pub to: &'a AccountView,
56    pub lamports: u64,
57}
58
59impl Transfer<'_> {
60    /// Invoke the Transfer instruction (no PDA signers).
61    #[inline]
62    pub fn invoke(&self) -> ProgramResult {
63        self.invoke_signed(&[])
64    }
65
66    /// Invoke the Transfer instruction with PDA signers.
67    #[inline]
68    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
69        // Instruction data: u32(2) + u64(lamports)
70        let mut data = [0u8; 12];
71        data[0] = 2;
72        data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
73
74        let accounts = [CpiAccount::from(self.from), CpiAccount::from(self.to)];
75
76        invoke_system(&data, &accounts, signers)
77    }
78}
79
80// ── Assign ───────────────────────────────────────────────────────────
81
82/// Builder for the system program's Assign instruction.
83pub struct Assign<'a, 'b> {
84    pub account: &'a AccountView,
85    pub owner: &'b Address,
86}
87
88impl Assign<'_, '_> {
89    /// Invoke the Assign instruction (no PDA signers).
90    #[inline]
91    pub fn invoke(&self) -> ProgramResult {
92        self.invoke_signed(&[])
93    }
94
95    /// Invoke the Assign instruction with PDA signers.
96    #[inline]
97    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
98        // Instruction data: u32(1) + [u8;32](owner)
99        let mut data = [0u8; 36];
100        data[0] = 1;
101        data[4..36].copy_from_slice(self.owner.as_array());
102
103        let accounts = [CpiAccount::from(self.account)];
104
105        invoke_system(&data, &accounts, signers)
106    }
107}
108
109// ── Allocate ─────────────────────────────────────────────────────────
110
111/// Builder for the system program's Allocate instruction.
112pub struct Allocate<'a> {
113    pub account: &'a AccountView,
114    pub space: u64,
115}
116
117impl Allocate<'_> {
118    /// Invoke the Allocate instruction (no PDA signers).
119    #[inline]
120    pub fn invoke(&self) -> ProgramResult {
121        self.invoke_signed(&[])
122    }
123
124    /// Invoke the Allocate instruction with PDA signers.
125    #[inline]
126    pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
127        // Instruction data: u32(8) + u64(space)
128        let mut data = [0u8; 12];
129        data[0] = 8;
130        data[4..12].copy_from_slice(&self.space.to_le_bytes());
131
132        let accounts = [CpiAccount::from(self.account)];
133
134        invoke_system(&data, &accounts, signers)
135    }
136}
137
138// ── Internal helper ──────────────────────────────────────────────────
139
140/// Build an InstructionView to the system program and invoke.
141#[inline]
142fn invoke_system(data: &[u8], accounts: &[CpiAccount], signers: &[Signer]) -> ProgramResult {
143    // Build an InstructionView to the system program and invoke via C ABI.
144    #[cfg(target_os = "solana")]
145    {
146        let ix = crate::instruction::InstructionView {
147            program_id: &SYSTEM_PROGRAM_ID,
148            data,
149            accounts: &[], // Not used by the C ABI path
150        };
151        let result = unsafe {
152            crate::syscalls::sol_invoke_signed_c(
153                &ix as *const _ as *const u8,
154                accounts.as_ptr() as *const u8,
155                accounts.len() as u64,
156                signers.as_ptr() as *const u8,
157                signers.len() as u64,
158            )
159        };
160        if result == 0 {
161            Ok(())
162        } else {
163            Err(crate::ProgramError::from(result))
164        }
165    }
166    #[cfg(not(target_os = "solana"))]
167    {
168        let _ = (data, accounts, signers);
169        Ok(())
170    }
171}
172
173/// Compatibility re-exports matching `pinocchio_system::instructions::*`.
174pub mod instructions {
175    pub use super::{Allocate, Assign, CreateAccount, Transfer};
176}