Skip to main content

magicblock_magic_program_api/
instruction.rs

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