magicsvm 0.2.0

A fast and lightweight Solana + MagicBlock VM simulator for testing solana programs
use {
    dlp_api::{
        args::CallHandlerArgs, consts::DELEGATION_PROGRAM_ID, discriminator::DlpDiscriminator,
    },
    magicblock_magic_program_api::args::BaseActionArgs,
    solana_address::Address,
    solana_message::{AccountMeta, Instruction},
};

/// Builds the base-layer DLP `CallHandler` instruction for a scheduled Magic Action.
///
/// `CallHandler` `invoke_signed`s the destination with the DLP escrow PDA as
/// signer. Destination metas are the scheduled accounts, then escrow authority,
/// then escrow — the layout `#[action]` handlers expect.
///
/// `CallHandlerV2` inserts `source_program` before those escrow accounts and
/// breaks deployed handlers, so Magicsvm executes every scheduled action with
/// `CallHandler`.
pub(super) fn base_action_instruction(
    action: &BaseActionArgs,
    escrow_authority: Address,
    validator: Address,
) -> Instruction {
    let other_accounts: Vec<AccountMeta> = action
        .accounts
        .iter()
        .map(|account| {
            let pubkey = Address::new_from_array(account.pubkey.to_bytes());
            if account.is_writable {
                AccountMeta::new(pubkey, false)
            } else {
                AccountMeta::new_readonly(pubkey, false)
            }
        })
        .collect();
    let validator_fees_vault = validator_fees_vault_pda(validator);
    let escrow = escrow_pda(escrow_authority, action.args.escrow_index);
    let destination = Address::new_from_array(action.destination_program.to_bytes());
    let mut accounts = vec![
        AccountMeta::new(validator, true),
        AccountMeta::new(validator_fees_vault, false),
        AccountMeta::new_readonly(destination, false),
        AccountMeta::new(escrow_authority, false),
        AccountMeta::new(escrow, false),
    ];
    accounts.extend(other_accounts);

    let mut data = DlpDiscriminator::CallHandler.to_vec();
    data.extend(
        borsh::to_vec(&CallHandlerArgs {
            escrow_index: action.args.escrow_index,
            data: action.args.data.clone(),
        })
        .expect("CallHandlerArgs is borsh-serializable"),
    );
    Instruction {
        program_id: DELEGATION_PROGRAM_ID,
        accounts,
        data,
    }
}

pub(super) fn escrow_pda(escrow_authority: Address, index: u8) -> Address {
    dlp_api::pda::ephemeral_balance_pda_from_payer(&escrow_authority.to_bytes().into(), index)
        .to_bytes()
        .into()
}

pub(super) fn validator_fees_vault_pda(validator: Address) -> Address {
    dlp_api::pda::validator_fees_vault_pda_from_validator(&validator.to_bytes().into())
        .to_bytes()
        .into()
}