marsh-program 2.1.2

Marsh is a cryptocurrency for sovereign individuals living in Mirascape Horizon.
Documentation
use marsh_api::{consts::*, error::MarshError, instruction::Stake, loaders::*, state::Config};
use marsh_utils::*;
use solana_program::{
    account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
    program_pack::Pack,
};
use spl_token::state::Mint;

/// Upgrade allows a user to migrate a v1(Mars) token to a v2(Marsh) token at a 1:1 exchange rate.
/// - The evolution must be activated, which means evolvable flag is true(non-zero)
pub fn process_upgrade(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
    // Parse args
    let args = Stake::try_from_bytes(data)?;
    let amount = u64::from_le_bytes(args.amount);

    // Load accounts.
    let [signer, beneficiary_info, mint_info, mint_v1_info, sender_info, treasury_info, token_program, config_info] =
        accounts
    else {
        return Err(ProgramError::NotEnoughAccountKeys);
    };
    load_signer(signer)?;
    load_token_account(beneficiary_info, Some(&signer.key), &MINT_ADDRESS, true)?;
    load_mint(mint_info, MINT_ADDRESS, true)?;
    load_mint(mint_v1_info, MINT_V1_ADDRESS, true)?;
    load_token_account(sender_info, Some(signer.key), &MINT_V1_ADDRESS, true)?;
    load_program(token_program, spl_token::id())?;
    load_config(config_info, false)?;

    // MI, Validate evolution has been activated.
    let config_data = config_info.data.borrow();
    let config = Config::try_from_bytes(&config_data)?;
    if config.evolvable.eq(&0) {
        return Err(MarshError::NotEvolvable.into());
    }

    // Burn v1(Mars) tokens.
    solana_program::program::invoke(
        &spl_token::instruction::burn(
            &spl_token::id(),
            sender_info.key,
            mint_v1_info.key,
            signer.key,
            &[signer.key],
            amount,
        )?,
        &[
            token_program.clone(),
            sender_info.clone(),
            mint_v1_info.clone(),
            signer.clone(),
        ],
    )?;

    // Account for decimals change.
    // v1(Mars) token has 8 decimals. v2(Marsh) token has 9 decimals.
    let amount_to_mint = amount.saturating_mul(10);

    // Cap at max supply.
    let mint_data = mint_info.data.borrow();
    let mint = Mint::unpack(&mint_data)?;
    if mint.supply.saturating_add(amount_to_mint).gt(&MAX_SUPPLY) {
        return Err(MarshError::MaxSupply.into());
    }

    // Mint to the beneficiary account
    drop(mint_data);
    mint_to_signed(
        mint_info,
        beneficiary_info,
        treasury_info,
        token_program,
        amount_to_mint,
        &[&[TREASURY, &[TREASURY_BUMP]]],
    )?;

    Ok(())
}