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