Skip to main content

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}