use {
magicblock_magic_program_api::{
args::{
BaseActionArgs, CommitAndUndelegateArgs, CommitTypeArgs, MagicBaseIntentArgs,
MagicIntentBundleArgs, UndelegateTypeArgs,
},
instruction::MagicBlockInstruction,
},
solana_transaction::InstructionError,
};
pub enum MagicInstruction {
ScheduleCommit,
ScheduleCommitAndUndelegate,
ScheduleCommitFinalize { request_undelegation: bool },
ScheduleBaseIntent {
committed_accounts: Vec<u8>,
undelegated_accounts: Vec<u8>,
base_actions: Vec<BaseActionArgs>,
},
ScheduleIntentBundle {
committed_accounts: Vec<u8>,
undelegated_accounts: Vec<u8>,
base_actions: Vec<BaseActionArgs>,
},
CreateEphemeralAccount { data_len: u32 },
ResizeEphemeralAccount { new_data_len: u32 },
CloseEphemeralAccount,
Noop,
}
pub fn magic_instruction(data: &[u8]) -> Result<MagicInstruction, InstructionError> {
let instruction: MagicBlockInstruction =
bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)?;
match instruction {
MagicBlockInstruction::ScheduleCommit => Ok(MagicInstruction::ScheduleCommit),
MagicBlockInstruction::ScheduleCommitAndUndelegate => {
Ok(MagicInstruction::ScheduleCommitAndUndelegate)
}
MagicBlockInstruction::ScheduleCommitFinalize {
request_undelegation,
} => Ok(MagicInstruction::ScheduleCommitFinalize {
request_undelegation,
}),
MagicBlockInstruction::ScheduleBaseIntent(args) => schedule_base_intent(args),
MagicBlockInstruction::ScheduleIntentBundle(args) => schedule_intent_bundle(args),
MagicBlockInstruction::CreateEphemeralAccount { data_len } => {
Ok(MagicInstruction::CreateEphemeralAccount { data_len })
}
MagicBlockInstruction::ResizeEphemeralAccount { new_data_len } => {
Ok(MagicInstruction::ResizeEphemeralAccount { new_data_len })
}
MagicBlockInstruction::CloseEphemeralAccount => Ok(MagicInstruction::CloseEphemeralAccount),
MagicBlockInstruction::Noop(_) => Ok(MagicInstruction::Noop),
_ => Err(InstructionError::InvalidInstructionData),
}
}
fn schedule_base_intent(args: MagicBaseIntentArgs) -> Result<MagicInstruction, InstructionError> {
match args {
MagicBaseIntentArgs::BaseActions(base_actions) => {
Ok(MagicInstruction::ScheduleBaseIntent {
committed_accounts: Vec::new(),
undelegated_accounts: Vec::new(),
base_actions,
})
}
MagicBaseIntentArgs::Commit(commit_type)
| MagicBaseIntentArgs::CommitFinalize(commit_type) => {
let (committed_accounts, base_actions) = commit_type_parts(commit_type);
Ok(MagicInstruction::ScheduleBaseIntent {
committed_accounts,
undelegated_accounts: Vec::new(),
base_actions,
})
}
MagicBaseIntentArgs::CommitAndUndelegate(args)
| MagicBaseIntentArgs::CommitFinalizeAndUndelegate(args) => {
let (undelegated_accounts, base_actions) = commit_and_undelegate_parts(args);
Ok(MagicInstruction::ScheduleBaseIntent {
committed_accounts: Vec::new(),
undelegated_accounts,
base_actions,
})
}
}
}
fn schedule_intent_bundle(
args: MagicIntentBundleArgs,
) -> Result<MagicInstruction, InstructionError> {
let mut committed_accounts = Vec::new();
let mut undelegated_accounts = Vec::new();
let mut base_actions = Vec::new();
if let Some(commit_type) = args.commit {
let (indices, actions) = commit_type_parts(commit_type);
committed_accounts.extend(indices);
base_actions.extend(actions);
}
if let Some(args) = args.commit_and_undelegate {
let (indices, actions) = commit_and_undelegate_parts(args);
undelegated_accounts.extend(indices);
base_actions.extend(actions);
}
if let Some(commit_type) = args.commit_finalize {
let (indices, actions) = commit_type_parts(commit_type);
committed_accounts.extend(indices);
base_actions.extend(actions);
}
if let Some(args) = args.commit_finalize_and_undelegate {
let (indices, actions) = commit_and_undelegate_parts(args);
undelegated_accounts.extend(indices);
base_actions.extend(actions);
}
base_actions.extend(args.standalone_actions);
Ok(MagicInstruction::ScheduleIntentBundle {
committed_accounts,
undelegated_accounts,
base_actions,
})
}
fn commit_type_parts(commit_type: CommitTypeArgs) -> (Vec<u8>, Vec<BaseActionArgs>) {
match commit_type {
CommitTypeArgs::Standalone(indices) => (indices, Vec::new()),
CommitTypeArgs::WithBaseActions {
committed_accounts,
base_actions,
} => (committed_accounts, base_actions),
}
}
fn commit_and_undelegate_parts(args: CommitAndUndelegateArgs) -> (Vec<u8>, Vec<BaseActionArgs>) {
let (indices, mut actions) = commit_type_parts(args.commit_type);
actions.extend(match args.undelegate_type {
UndelegateTypeArgs::Standalone => Vec::new(),
UndelegateTypeArgs::WithBaseActions { base_actions } => base_actions,
});
(indices, actions)
}