magicblock_magic_program_api/instruction.rs
1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use solana_program::{instruction::Instruction, pubkey::Pubkey};
5
6use crate::args::{
7 AddActionCallbackArgs, MagicBaseIntentArgs, MagicIntentBundleArgs,
8 ScheduleTaskArgs,
9};
10
11#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
12pub enum MagicBlockInstruction {
13 /// Modify one or more accounts
14 ///
15 /// # Account references
16 /// - **0.** `[WRITE, SIGNER]` Validator Authority
17 /// - **1..n.** `[WRITE]` Accounts to modify
18 /// - **n+1** `[SIGNER]` (Implicit NativeLoader)
19 ModifyAccounts {
20 accounts: HashMap<Pubkey, AccountModificationForInstruction>,
21 message: Option<String>,
22 },
23
24 /// Schedules the accounts provided at end of accounts Vec to be committed
25 /// and finalized in a single DLP instruction.
26 /// It should be invoked from the program whose PDA accounts are to be
27 /// committed.
28 ///
29 /// This is the first part of scheduling a commit.
30 /// A second transaction [MagicBlockInstruction::AcceptScheduleCommits] has to run in order
31 /// to finish scheduling the commit.
32 ///
33 /// # Account references
34 /// - **0.** `[WRITE, SIGNER]` Payer requesting the commit to be scheduled
35 /// - **1.** `[WRITE]` Magic Context Account containing to which we store
36 /// the scheduled commits
37 /// - **2..n** `[]` Accounts to be committed
38 ScheduleCommit,
39
40 /// This is the exact same instruction as [MagicBlockInstruction::ScheduleCommit] except
41 /// that the scheduled intent is flagged such that when accounts are committed and finalized,
42 /// a request to undelegate them is included with the same transaction.
43 /// Additionally the validator will refuse anymore transactions for the specific account
44 /// since they are no longer considered delegated to it.
45 ///
46 /// This is the first part of scheduling a commit.
47 /// A second transaction [MagicBlockInstruction::AcceptScheduleCommits] has to run in order
48 /// to finish scheduling the commit.
49 ///
50 /// # Account references
51 /// - **0.** `[WRITE, SIGNER]` Payer requesting the commit to be scheduled
52 /// - **1.** `[WRITE]` Magic Context Account containing to which we store
53 /// the scheduled commits
54 /// - **2..n** `[]` Accounts to be committed and undelegated
55 ScheduleCommitAndUndelegate,
56
57 /// Moves the scheduled commit from the MagicContext to the global scheduled commits
58 /// map. This is the second part of scheduling a commit.
59 ///
60 /// It is run at the start of the slot to update the global scheduled commits map just
61 /// in time for the validator to realize the commits right after.
62 ///
63 /// # Account references
64 /// - **0.** `[SIGNER]` Validator Authority
65 /// - **1.** `[WRITE]` Magic Context Account containing the initially scheduled commits
66 AcceptScheduleCommits,
67
68 /// Records the attempt to realize a scheduled commit on chain.
69 ///
70 /// The signature of this transaction can be pre-calculated since we pass the
71 /// ID of the scheduled commit and retrieve the signature from a globally
72 /// stored hashmap.
73 ///
74 /// We implement it this way so we can log the signature of this transaction
75 /// as part of the [MagicBlockInstruction::ScheduleCommit] instruction.
76 /// Args: (intent_id, bump) - bump is needed in order to guarantee unique transactions
77 ScheduledCommitSent((u64, u64)),
78
79 /// Schedules execution of a single *base intent*.
80 ///
81 /// A "base intent" is an atomic unit of work executed by the validator on the Base layer,
82 /// such as:
83 /// - executing standalone base actions (`BaseActions`)
84 /// - committing a set of accounts (`Commit`)
85 /// - committing and undelegating accounts, optionally with post-actions (`CommitAndUndelegate`)
86 ///
87 /// This instruction is the legacy/single-intent variant of scheduling. For batching multiple
88 /// independent intents into a single instruction, see [`MagicBlockInstruction::ScheduleIntentBundle`].
89 ///
90 /// # Account references
91 /// - **0.** `[WRITE, SIGNER]` Payer requesting the intent to be scheduled
92 /// - **1.** `[WRITE]` Magic Context account
93 /// - **2..n** `[]` Accounts referenced by the intent (including action accounts)
94 ///
95 /// # Data
96 /// The embedded [`MagicBaseIntentArgs`] encodes account references by indices into the
97 /// accounts array (compact representation).
98 ScheduleBaseIntent(MagicBaseIntentArgs),
99
100 /// Schedule a new task for execution
101 ///
102 /// # Account references
103 /// - **0.** `[WRITE, SIGNER]` Payer (payer)
104 /// - **1.** `[WRITE]` Task context account
105 /// - **2..n** `[]` Accounts included in the task
106 ScheduleTask(ScheduleTaskArgs),
107
108 /// Cancel a task
109 ///
110 /// # Account references
111 /// - **0.** `[WRITE, SIGNER]` Task authority
112 /// - **1.** `[WRITE]` Task context account
113 CancelTask { task_id: i64 },
114
115 /// Disables the executable check, needed to modify the data of a program
116 /// in preparation to deploying it via LoaderV4 and to modify its authority.
117 ///
118 /// # Account references
119 /// - **0.** `[SIGNER]` Validator authority
120 DisableExecutableCheck,
121
122 /// Enables the executable check, and should run after
123 /// a program is deployed with the LoaderV4 and we modified its authority
124 ///
125 /// # Account references
126 /// - **0.** `[SIGNER]` Validator authority
127 EnableExecutableCheck,
128
129 /// Noop instruction
130 Noop(u64),
131
132 /// Schedules execution of a *bundle* of intents in a single instruction.
133 ///
134 /// A "intent bundle" is an atomic unit of work executed by the validator on the Base layer,
135 /// such as:
136 /// - standalone base actions
137 /// - an optional `Commit`
138 /// - an optional `CommitAndUndelegate`
139 ///
140 /// This is the recommended scheduling path when the caller wants to submit multiple
141 /// independent intents while paying account overhead only once.
142 ///
143 /// # Account references
144 /// - **0.** `[WRITE, SIGNER]` Payer requesting the bundle to be scheduled
145 /// - **1.** `[WRITE]` Magic Context account
146 /// - **2..n** `[]` All accounts referenced by any intent in the bundle
147 ///
148 /// # Data
149 /// The embedded [`MagicIntentBundleArgs`] encodes account references by indices into the
150 /// accounts array.
151 ScheduleIntentBundle(MagicIntentBundleArgs),
152
153 /// Creates a new ephemeral account with rent paid by a sponsor.
154 /// The account is automatically owned by the calling program (CPI caller).
155 ///
156 /// # Account references
157 /// - **0.** `[WRITE]` Sponsor account (pays rent, can be PDA or oncurve)
158 /// - **1.** `[WRITE]` Ephemeral account to create (must have 0 lamports)
159 /// - **2.** `[WRITE]` Vault account (receives rent payment)
160 CreateEphemeralAccount {
161 /// Initial data length in bytes
162 data_len: u32,
163 },
164
165 /// Resizes an existing ephemeral account, adjusting rent accordingly.
166 ///
167 /// # Account references
168 /// - **0.** `[WRITE]` Sponsor account (pays/receives rent difference)
169 /// - **1.** `[WRITE]` Ephemeral account to resize
170 /// - **2.** `[WRITE]` Vault account (holds/receives lamports for rent transfer)
171 ResizeEphemeralAccount {
172 /// New data length in bytes
173 new_data_len: u32,
174 },
175
176 /// Closes an ephemeral account, refunding rent to the sponsor.
177 ///
178 /// # Account references
179 /// - **0.** `[WRITE]` Sponsor account (receives rent refund)
180 /// - **1.** `[WRITE]` Ephemeral account to close
181 /// - **2.** `[WRITE]` Vault account (source of rent refund)
182 CloseEphemeralAccount,
183
184 /// Unsed instruction slot.
185 /// -- can be repurposed --
186 /// This variant was originally used for `ScheduleCommitFinalize`, but that
187 /// instruction was removed. It is intentionally left unused so the wire
188 /// discriminant can be repurposed in a future protocol update.
189 Unused,
190
191 /// Clone a single account that fits in one transaction (<63KB data).
192 ///
193 /// # Account references
194 /// - **0.** `[WRITE, SIGNER]` Validator Authority
195 /// - **1.** `[WRITE]` Account to clone
196 CloneAccount {
197 pubkey: Pubkey,
198 data: Vec<u8>,
199 fields: AccountCloneFields,
200 actions_tx_sig: Option<String>,
201 },
202
203 /// Initialize a multi-transaction clone for a large account.
204 /// Adds the pubkey to PENDING_CLONES. Must be followed by CloneAccountContinue
205 /// with is_last=true to complete.
206 ///
207 /// # Account references
208 /// - **0.** `[WRITE, SIGNER]` Validator Authority
209 /// - **1.** `[WRITE]` Account to clone
210 CloneAccountInit {
211 pubkey: Pubkey,
212 total_data_len: u32,
213 initial_data: Vec<u8>,
214 fields: AccountCloneFields,
215 actions_tx_sig: Option<String>,
216 },
217
218 /// Continue a multi-transaction clone with the next data chunk.
219 /// If is_last=true, removes the pubkey from PENDING_CLONES.
220 ///
221 /// # Account references
222 /// - **0.** `[WRITE, SIGNER]` Validator Authority
223 /// - **1.** `[WRITE]` Account being cloned
224 CloneAccountContinue {
225 pubkey: Pubkey,
226 offset: u32,
227 data: Vec<u8>,
228 is_last: bool,
229 },
230
231 /// Cleanup a partial clone on failure. Removes from PENDING_CLONES
232 /// and deletes the account.
233 ///
234 /// # Account references
235 /// - **0.** `[WRITE, SIGNER]` Validator Authority
236 /// - **1.** `[WRITE]` Account to cleanup
237 CleanupPartialClone { pubkey: Pubkey },
238
239 /// Finalize program deployment from a buffer account.
240 /// Does the following:
241 /// 1. Copies data from buffer account to program account
242 /// 2. Sets loader header with Retracted status and validator authority
243 /// 3. Closes buffer account
244 ///
245 /// After this, LoaderV4::Deploy must be called, then SetProgramAuthority.
246 ///
247 /// # Account references
248 /// - **0.** `[SIGNER]` Validator Authority
249 /// - **1.** `[WRITE]` Program account
250 /// - **2.** `[WRITE]` Buffer account (closed after)
251 FinalizeProgramFromBuffer { remote_slot: u64 },
252
253 /// Finalize V1 program deployment from a buffer account.
254 /// V1 programs are converted to V3 (upgradeable loader) format.
255 /// Does the following:
256 /// 1. Creates program_data account with V3 ProgramData header + ELF
257 /// 2. Creates program account with V3 Program header
258 /// 3. Closes buffer account
259 ///
260 /// # Account references
261 /// - **0.** `[SIGNER]` Validator Authority
262 /// - **1.** `[WRITE]` Program account
263 /// - **2.** `[WRITE]` Program data account
264 /// - **3.** `[WRITE]` Buffer account (closed after)
265 FinalizeV1ProgramFromBuffer { remote_slot: u64, authority: Pubkey },
266
267 /// Update the authority in a LoaderV4 program header.
268 /// Used after Deploy to set the final chain authority.
269 ///
270 /// # Account references
271 /// - **0.** `[SIGNER]` Validator Authority
272 /// - **1.** `[WRITE]` Program account
273 SetProgramAuthority { authority: Pubkey },
274
275 /// Attaches a callback to a previously scheduled action in the latest intent.
276 ///
277 /// Must be called via CPI from the program that originally scheduled the
278 /// action. The caller's program ID is checked against the action's
279 /// `source_program` field for authorization.
280 ///
281 /// If the payer account is delegated, a callback fee is deducted from it.
282 ///
283 /// # Account references
284 /// - **0.** `[WRITE, SIGNER]` Payer
285 /// - **1.** `[WRITE]` Magic Context account
286 AddActionCallback(AddActionCallbackArgs),
287
288 /// Evict an account from the ephemeral validator.
289 /// Sets the account to empty state (lamports=0, data=[], owner=default,
290 /// delegated=false, confined=false, ephemeral=true).
291 /// The ephemeral+default-owner combination triggers automatic removal
292 /// from AccountsDb during commit (see AccountsDb::upsert).
293 /// Rejects accounts that are delegated or undelegating.
294 ///
295 /// # Account references
296 /// - **0.** `[SIGNER]` Validator Authority
297 /// - **1.** `[WRITE]` Account to evict
298 EvictAccount { pubkey: Pubkey },
299
300 /// Executes a crank
301 ///
302 /// # Account references
303 /// - **0.** `[SIGNER]` Validator authority
304 /// - **1.** `[]` Crank signer PDA
305 /// - **2..n** `[]` Accounts required by the embedded instructions
306 ExecuteCrank { instructions: Vec<Instruction> },
307}
308
309impl MagicBlockInstruction {
310 pub fn try_to_vec(&self) -> Result<Vec<u8>, bincode::Error> {
311 bincode::serialize(self)
312 }
313}
314
315#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
316pub struct AccountModification {
317 pub pubkey: Pubkey,
318 pub owner: Option<Pubkey>,
319 pub delegated: Option<bool>,
320 pub confined: Option<bool>,
321}
322
323#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
324pub struct AccountModificationForInstruction {
325 pub owner: Option<Pubkey>,
326 pub delegated: Option<bool>,
327 pub confined: Option<bool>,
328}
329
330/// Common fields for cloning an account.
331#[derive(
332 Default, Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq,
333)]
334pub struct AccountCloneFields {
335 pub lamports: u64,
336 pub owner: Pubkey,
337 pub executable: bool,
338 pub delegated: bool,
339 pub confined: bool,
340 pub remote_slot: u64,
341}
342
343/// Instruction(s) for Callback Executor builtin-program
344#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
345pub enum CallbackInstruction {
346 /// Executes a callback
347 ///
348 /// # Account references
349 /// - **0.** `[SIGNER]` Validator authority
350 /// - **1.** `[]` Callback signer PDA
351 /// - **2..n** `[]` Accounts required by the embedded instructions
352 ExecuteCallback { instruction: Instruction },
353}