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    MagicBaseIntentArgs, MagicIntentBundleArgs, ScheduleTaskArgs,
8};
9
10#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
11pub enum MagicBlockInstruction {
12    /// Modify one or more accounts
13    ///
14    /// # Account references
15    ///  - **0.**    `[WRITE, SIGNER]` Validator Authority
16    ///  - **1..n.** `[WRITE]` Accounts to modify
17    ///  - **n+1**  `[SIGNER]` (Implicit NativeLoader)
18    ModifyAccounts {
19        accounts: HashMap<Pubkey, AccountModificationForInstruction>,
20        message: Option<String>,
21    },
22
23    /// Schedules the accounts provided at end of accounts Vec to be committed.
24    /// It should be invoked from the program whose PDA accounts are to be
25    /// committed.
26    ///
27    /// This is the first part of scheduling a commit.
28    /// A second transaction [MagicBlockInstruction::AcceptScheduleCommits] has to run in order
29    /// to finish scheduling the commit.
30    ///
31    /// # Account references
32    /// - **0.**   `[WRITE, SIGNER]` Payer requesting the commit to be scheduled
33    /// - **1.**   `[WRITE]`         Magic Context Account containing to which we store
34    ///   the scheduled commits
35    /// - **2..n** `[]`              Accounts to be committed
36    ScheduleCommit,
37
38    /// This is the exact same instruction as [MagicBlockInstruction::ScheduleCommit] except
39    /// that the [ScheduledCommit] is flagged such that when accounts are committed, a request
40    /// to undelegate them is included with the same transaction.
41    /// Additionally the validator will refuse anymore transactions for the specific account
42    /// since they are no longer considered delegated to it.
43    ///
44    /// This is the first part of scheduling a commit.
45    /// A second transaction [MagicBlockInstruction::AcceptScheduleCommits] has to run in order
46    /// to finish scheduling the commit.
47    ///
48    /// # Account references
49    /// - **0.**   `[WRITE, SIGNER]` Payer requesting the commit to be scheduled
50    /// - **1.**   `[WRITE]`         Magic Context Account containing to which we store
51    ///   the scheduled commits
52    /// - **2..n** `[]`              Accounts to be committed and undelegated
53    ScheduleCommitAndUndelegate,
54
55    /// Moves the scheduled commit from the MagicContext to the global scheduled commits
56    /// map. This is the second part of scheduling a commit.
57    ///
58    /// It is run at the start of the slot to update the global scheduled commits map just
59    /// in time for the validator to realize the commits right after.
60    ///
61    /// # Account references
62    /// - **0.**  `[SIGNER]` Validator Authority
63    /// - **1.**  `[WRITE]`  Magic Context Account containing the initially scheduled commits
64    AcceptScheduleCommits,
65
66    /// Records the attempt to realize a scheduled commit on chain.
67    ///
68    /// The signature of this transaction can be pre-calculated since we pass the
69    /// ID of the scheduled commit and retrieve the signature from a globally
70    /// stored hashmap.
71    ///
72    /// We implement it this way so we can log the signature of this transaction
73    /// as part of the [MagicBlockInstruction::ScheduleCommit] instruction.
74    /// Args: (intent_id, bump) - bump is needed in order to guarantee unique transactions
75    ScheduledCommitSent((u64, u64)),
76
77    /// Schedules execution of a single *base intent*.
78    ///
79    /// A "base intent" is an atomic unit of work executed by the validator on the Base layer,
80    /// such as:
81    /// - executing standalone base actions (`BaseActions`)
82    /// - committing a set of accounts (`Commit`)
83    /// - committing and undelegating accounts, optionally with post-actions (`CommitAndUndelegate`)
84    ///
85    /// This instruction is the legacy/single-intent variant of scheduling. For batching multiple
86    /// independent intents into a single instruction, see [`MagicBlockInstruction::ScheduleIntentBundle`].
87    ///
88    /// # Account references
89    /// - **0.**   `[WRITE, SIGNER]` Payer requesting the intent to be scheduled
90    /// - **1.**   `[WRITE]`         Magic Context account
91    /// - **2..n** `[]`              Accounts referenced by the intent (including action accounts)
92    ///
93    /// # Data
94    /// The embedded [`MagicBaseIntentArgs`] encodes account references by indices into the
95    /// accounts array (compact representation).
96    ScheduleBaseIntent(MagicBaseIntentArgs),
97
98    /// Schedule a new task for execution
99    ///
100    /// # Account references
101    /// - **0.**    `[WRITE, SIGNER]` Payer (payer)
102    /// - **1.**    `[WRITE]`         Task context account
103    /// - **2..n**  `[]`              Accounts included in the task
104    ScheduleTask(ScheduleTaskArgs),
105
106    /// Cancel a task
107    ///
108    /// # Account references
109    /// - **0.** `[WRITE, SIGNER]` Task authority
110    /// - **1.** `[WRITE]`         Task context account
111    CancelTask { task_id: i64 },
112
113    /// Disables the executable check, needed to modify the data of a program
114    /// in preparation to deploying it via LoaderV4 and to modify its authority.
115    ///
116    /// # Account references
117    /// - **0.** `[SIGNER]`         Validator authority
118    DisableExecutableCheck,
119
120    /// Enables the executable check, and should run after
121    /// a program is deployed with the LoaderV4 and we modified its authority
122    ///
123    /// # Account references
124    /// - **0.** `[SIGNER]`         Validator authority
125    EnableExecutableCheck,
126
127    /// Noop instruction
128    Noop(u64),
129
130    /// Schedules execution of a *bundle* of intents in a single instruction.
131    ///
132    /// A "intent bundle" is an atomic unit of work executed by the validator on the Base layer,
133    /// such as:
134    /// - standalone base actions
135    /// - an optional `Commit`
136    /// - an optional `CommitAndUndelegate`
137    ///
138    /// This is the recommended scheduling path when the caller wants to submit multiple
139    /// independent intents while paying account overhead only once.
140    ///
141    /// # Account references
142    /// - **0.**   `[WRITE, SIGNER]` Payer requesting the bundle to be scheduled
143    /// - **1.**   `[WRITE]`         Magic Context account
144    /// - **2..n** `[]`              All accounts referenced by any intent in the bundle
145    ///
146    /// # Data
147    /// The embedded [`MagicIntentBundleArgs`] encodes account references by indices into the
148    /// accounts array.
149    ScheduleIntentBundle(MagicIntentBundleArgs),
150
151    /// Creates a new ephemeral account with rent paid by a sponsor.
152    /// The account is automatically owned by the calling program (CPI caller).
153    ///
154    /// # Account references
155    /// - **0.** `[WRITE]` Sponsor account (pays rent, can be PDA or oncurve)
156    /// - **1.** `[WRITE]` Ephemeral account to create (must have 0 lamports)
157    /// - **2.** `[WRITE]` Vault account (receives rent payment)
158    CreateEphemeralAccount {
159        /// Initial data length in bytes
160        data_len: u32,
161    },
162
163    /// Resizes an existing ephemeral account, adjusting rent accordingly.
164    ///
165    /// # Account references
166    /// - **0.** `[WRITE]` Sponsor account (pays/receives rent difference)
167    /// - **1.** `[WRITE]` Ephemeral account to resize
168    /// - **2.** `[WRITE]` Vault account (holds/receives lamports for rent transfer)
169    ResizeEphemeralAccount {
170        /// New data length in bytes
171        new_data_len: u32,
172    },
173
174    /// Closes an ephemeral account, refunding rent to the sponsor.
175    ///
176    /// # Account references
177    /// - **0.** `[WRITE]` Sponsor account (receives rent refund)
178    /// - **1.** `[WRITE]` Ephemeral account to close
179    /// - **2.** `[WRITE]` Vault account (source of rent refund)
180    CloseEphemeralAccount,
181
182    /// Schedules the accounts provided at end of accounts Vec to be committed and finalized in a
183    /// single DLP instruction.
184    /// It should be invoked from the program whose PDA accounts are to be
185    /// committed.
186    ///
187    /// This is the first part of scheduling a commit.
188    /// A second transaction [MagicBlockInstruction::AcceptScheduleCommits] has to run in order
189    /// to finish scheduling the commit.
190    ///
191    /// # Account references
192    /// - **0.**   `[WRITE, SIGNER]` Payer requesting the commit to be scheduled
193    /// - **1.**   `[WRITE]`         Magic Context Account containing to which we store
194    ///   the scheduled commits
195    /// - **2..n** `[]`              Accounts to be committed
196    ScheduleCommitFinalize { request_undelegation: bool },
197    /// Clone a single account that fits in one transaction (<63KB data).
198    ///
199    /// # Account references
200    /// - **0.** `[WRITE, SIGNER]` Validator Authority
201    /// - **1.** `[WRITE]` Account to clone
202    CloneAccount {
203        pubkey: Pubkey,
204        data: Vec<u8>,
205        fields: AccountCloneFields,
206    },
207
208    /// Initialize a multi-transaction clone for a large account.
209    /// Adds the pubkey to PENDING_CLONES. Must be followed by CloneAccountContinue
210    /// with is_last=true to complete.
211    ///
212    /// # Account references
213    /// - **0.** `[WRITE, SIGNER]` Validator Authority
214    /// - **1.** `[WRITE]` Account to clone
215    CloneAccountInit {
216        pubkey: Pubkey,
217        total_data_len: u32,
218        initial_data: Vec<u8>,
219        fields: AccountCloneFields,
220    },
221
222    /// Continue a multi-transaction clone with the next data chunk.
223    /// If is_last=true, removes the pubkey from PENDING_CLONES.
224    ///
225    /// # Account references
226    /// - **0.** `[WRITE, SIGNER]` Validator Authority
227    /// - **1.** `[WRITE]` Account being cloned
228    CloneAccountContinue {
229        pubkey: Pubkey,
230        offset: u32,
231        data: Vec<u8>,
232        is_last: 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
280impl MagicBlockInstruction {
281    pub fn try_to_vec(&self) -> Result<Vec<u8>, bincode::Error> {
282        bincode::serialize(self)
283    }
284}
285
286#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
287pub struct AccountModification {
288    pub pubkey: Pubkey,
289    pub lamports: Option<u64>,
290    pub owner: Option<Pubkey>,
291    pub executable: Option<bool>,
292    pub data: Option<Vec<u8>>,
293    pub delegated: Option<bool>,
294    pub confined: Option<bool>,
295    pub remote_slot: Option<u64>,
296}
297
298#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
299pub struct AccountModificationForInstruction {
300    pub lamports: Option<u64>,
301    pub owner: Option<Pubkey>,
302    pub executable: Option<bool>,
303    pub data: Option<Vec<u8>>,
304    pub delegated: Option<bool>,
305    pub confined: Option<bool>,
306    pub remote_slot: Option<u64>,
307}
308
309/// Common fields for cloning an account.
310#[derive(
311    Default, Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq,
312)]
313pub struct AccountCloneFields {
314    pub lamports: u64,
315    pub owner: Pubkey,
316    pub executable: bool,
317    pub delegated: bool,
318    pub confined: bool,
319    pub remote_slot: u64,
320}