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}