Skip to main content

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: Vec<Instruction>,
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    },
219
220    /// Continue a multi-transaction clone with the next data chunk.
221    /// If is_last=true, removes the pubkey from PENDING_CLONES.
222    ///
223    /// # Account references
224    /// - **0.** `[WRITE, SIGNER]` Validator Authority
225    /// - **1.** `[WRITE]` Account being cloned
226    CloneAccountContinue {
227        pubkey: Pubkey,
228        offset: u32,
229        data: Vec<u8>,
230        is_last: bool,
231        actions: Vec<Instruction>,
232        needs_undelegation: bool,
233    },
234
235    /// Cleanup a partial clone on failure. Removes from PENDING_CLONES
236    /// and deletes the account.
237    ///
238    /// # Account references
239    /// - **0.** `[WRITE, SIGNER]` Validator Authority
240    /// - **1.** `[WRITE]` Account to cleanup
241    CleanupPartialClone { pubkey: Pubkey },
242
243    /// Finalize program deployment from a buffer account.
244    /// Does the following:
245    /// 1. Copies data from buffer account to program account
246    /// 2. Sets loader header with Retracted status and validator authority
247    /// 3. Closes buffer account
248    ///
249    /// After this, LoaderV4::Deploy must be called, then SetProgramAuthority.
250    ///
251    /// # Account references
252    /// - **0.** `[SIGNER]` Validator Authority
253    /// - **1.** `[WRITE]` Program account
254    /// - **2.** `[WRITE]` Buffer account (closed after)
255    FinalizeProgramFromBuffer { remote_slot: u64 },
256
257    /// Finalize V1 program deployment from a buffer account.
258    /// V1 programs are converted to V3 (upgradeable loader) format.
259    /// Does the following:
260    /// 1. Creates program_data account with V3 ProgramData header + ELF
261    /// 2. Creates program account with V3 Program header
262    /// 3. Closes buffer account
263    ///
264    /// # Account references
265    /// - **0.** `[SIGNER]` Validator Authority
266    /// - **1.** `[WRITE]` Program account
267    /// - **2.** `[WRITE]` Program data account
268    /// - **3.** `[WRITE]` Buffer account (closed after)
269    FinalizeV1ProgramFromBuffer { remote_slot: u64, authority: Pubkey },
270
271    /// Update the authority in a LoaderV4 program header.
272    /// Used after Deploy to set the final chain authority.
273    ///
274    /// # Account references
275    /// - **0.** `[SIGNER]` Validator Authority
276    /// - **1.** `[WRITE]` Program account
277    SetProgramAuthority { authority: Pubkey },
278
279    /// Attaches a callback to a previously scheduled action in the latest intent.
280    ///
281    /// Must be called via CPI from the program that originally scheduled the
282    /// action. The caller's program ID is checked against the action's
283    /// `source_program` field for authorization.
284    ///
285    /// If the payer account is delegated, a callback fee is deducted from it.
286    ///
287    /// # Account references
288    /// - **0.**   `[WRITE, SIGNER]` Payer
289    /// - **1.**   `[WRITE]`         Magic Context account
290    AddActionCallback(AddActionCallbackArgs),
291
292    /// Evict an account from the ephemeral validator.
293    /// Sets the account to empty state (lamports=0, data=[], owner=default,
294    /// delegated=false, confined=false, ephemeral=true).
295    /// The ephemeral+default-owner combination triggers automatic removal
296    /// from AccountsDb during commit (see AccountsDb::upsert).
297    /// Rejects accounts that are delegated or undelegating.
298    ///
299    /// # Account references
300    /// - **0.** `[SIGNER]`  Validator Authority
301    /// - **1.** `[WRITE]`   Account to evict
302    EvictAccount { pubkey: Pubkey },
303
304    /// Executes a crank
305    ///
306    /// # Account references
307    /// - **0.**   `[SIGNER]`  Validator authority
308    /// - **1.**   `[]`        Crank signer PDA
309    /// - **2..n** `[]`        Accounts required by the embedded instructions
310    ExecuteCrank {
311        authority: Pubkey,
312        instructions: Vec<Instruction>,
313    },
314}
315
316impl MagicBlockInstruction {
317    pub fn try_to_vec(&self) -> Result<Vec<u8>, bincode::Error> {
318        bincode::serialize(self)
319    }
320}
321
322#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
323pub struct AccountModification {
324    pub pubkey: Pubkey,
325    pub owner: Option<Pubkey>,
326    pub delegated: Option<bool>,
327    pub confined: Option<bool>,
328}
329
330#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
331pub struct AccountModificationForInstruction {
332    pub owner: Option<Pubkey>,
333    pub delegated: Option<bool>,
334    pub confined: Option<bool>,
335}
336
337/// Common fields for cloning an account.
338#[derive(
339    Default, Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq,
340)]
341pub struct AccountCloneFields {
342    pub lamports: u64,
343    pub owner: Pubkey,
344    pub executable: bool,
345    pub delegated: bool,
346    pub confined: bool,
347    pub remote_slot: u64,
348}
349
350/// Instruction(s) for Callback Executor builtin-program
351#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
352pub enum CallbackInstruction {
353    /// Executes a callback
354    ///
355    /// # Account references
356    /// - **0.**   `[SIGNER]`  Validator authority
357    /// - **1.**   `[]`        Callback signer PDA
358    /// - **2..n** `[]`        Accounts required by the embedded instructions
359    ExecuteCallback { instruction: Instruction },
360}
361
362/// Instruction(s) for the post-delegation action executor builtin-program.
363#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
364pub enum PostDelegationActionExecutorInstruction {
365    /// Executes post-delegation actions immediately after a matching delegated
366    /// clone instruction in the same transaction.
367    ///
368    /// # Account references
369    /// - **0.**   `[SIGNER]`  Validator authority
370    /// - **1.**   `[]`        Delegated clone target
371    /// - **2.**   `[]`        Instructions sysvar
372    /// - **3..n** `[]`        Accounts required by the embedded instructions
373    Execute {
374        cloned_account_pubkey: Pubkey,
375        actions: Vec<Instruction>,
376    },
377
378    /// Schedules undelegation immediately after a matching delegated clone
379    /// instruction in the same transaction.
380    ///
381    /// # Account references
382    /// - **0.**   `[SIGNER]`  Validator authority
383    /// - **1.**   `[]`        Delegated clone target
384    /// - **2.**   `[]`        Instructions sysvar
385    /// - **3.**   `[WRITE]`   Magic Context account
386    ScheduleUndelegation { cloned_account_pubkey: Pubkey },
387}