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