community-managed-token 0.3.1

Community Managed Token
Documentation
solana_program::declare_id!("CMTQqjzH6Anr9XcPVt73EFDTjWkJWPzH7H6DtvhHcyzV");

use borsh::BorshDeserialize;
use solana_program::{
    account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke,
    program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent,
    system_instruction, sysvar::Sysvar,
};
use spl_associated_token_account::instruction::create_associated_token_account;

#[track_caller]
#[inline(always)]
pub fn assert_with_msg(v: bool, err: impl Into<ProgramError>, msg: &str) -> ProgramResult {
    if v {
        Ok(())
    } else {
        let caller = std::panic::Location::caller();
        msg!("{}. \n{}", msg, caller);
        Err(err.into())
    }
}

pub mod accounts;
pub mod instruction;
pub mod token;
use accounts::{
    Approve, Burn, Close, InitializeAccount, InitializeMint, MigrateAuthority, Mint, Revoke,
    Transfer,
};
use instruction::ManagedTokenInstruction;
use spl_token::instruction::AuthorityType;
use token::{
    approve, burn, close, freeze, initialize_mint, mint_to, revoke, set_authority, thaw, transfer,
};

#[cfg(not(feature = "no-entrypoint"))]
solana_program::entrypoint!(process_instruction);

#[inline]
fn get_authority_seeds_checked(
    upstream_authority: &Pubkey,
    expected_key: &Pubkey,
) -> Result<Vec<Vec<u8>>, ProgramError> {
    let (key, seeds) = get_authority(upstream_authority);
    assert_with_msg(
        expected_key == &key,
        ProgramError::InvalidInstructionData,
        "Invalid authority",
    )?;
    Ok(seeds)
}

#[inline]
fn get_authority(upstream_authority: &Pubkey) -> (Pubkey, Vec<Vec<u8>>) {
    let mut seeds = vec![upstream_authority.as_ref().to_vec()];
    let (key, bump) = Pubkey::find_program_address(
        &seeds.iter().map(|s| s.as_slice()).collect::<Vec<&[u8]>>(),
        &crate::id(),
    );
    seeds.push(vec![bump]);
    (key, seeds)
}

pub fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = ManagedTokenInstruction::try_from_slice(instruction_data)?;
    match instruction {
        ManagedTokenInstruction::InitializeMint { decimals } => {
            msg!("ManagedTokenInstruction::InitializeMint");
            process_initialize_mint(accounts, decimals)
        }
        ManagedTokenInstruction::InitializeAccount => {
            msg!("ManagedTokenInstruction::InitializeAccount");
            process_initialize_account(accounts)
        }
        ManagedTokenInstruction::Transfer { amount } => {
            msg!("ManagedTokenInstruction::Transfer");
            process_transfer(accounts, amount)
        }
        ManagedTokenInstruction::MintTo { amount } => {
            msg!("ManagedTokenInstruction::MintTo");
            process_mint_to(accounts, amount)
        }
        ManagedTokenInstruction::Burn { amount } => {
            msg!("ManagedTokenInstruction::Burn");
            process_burn(accounts, amount)
        }
        ManagedTokenInstruction::CloseAccount => {
            msg!("ManagedTokenInstruction::CloseAccount");
            process_close(accounts)
        }
        ManagedTokenInstruction::Approve { amount } => {
            msg!("ManagedTokenInstruction::Approve");
            process_approve(accounts, amount)
        }
        ManagedTokenInstruction::Revoke => {
            msg!("ManagedTokenInstruction::Revoke");
            process_revoke(accounts)
        }
        ManagedTokenInstruction::MigrateAuthority => {
            msg!("ManagedTokenInstruction::MigrationAuthority");
            process_migrate_authority(accounts)
        }
    }
}

pub fn process_initialize_mint(accounts: &[AccountInfo], decimals: u8) -> ProgramResult {
    let InitializeMint {
        mint,
        payer,
        upstream_authority,
        system_program,
        token_program,
    } = InitializeMint::load(accounts)?;
    let space = spl_token::state::Mint::LEN;
    invoke(
        &system_instruction::create_account(
            payer.key,
            mint.key,
            Rent::get()?.minimum_balance(space),
            space as u64,
            token_program.key,
        ),
        &[payer.clone(), mint.clone(), system_program.clone()],
    )?;
    let (authority, _) = get_authority(upstream_authority.key);
    initialize_mint(&authority, &authority, mint, token_program, decimals)
}

pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
    let InitializeAccount {
        token_account,
        owner,
        payer,
        upstream_authority,
        freeze_authority,
        mint,
        system_program,
        associated_token_program,
        token_program,
    } = InitializeAccount::load(accounts)?;
    invoke(
        &create_associated_token_account(payer.key, owner.key, mint.key, token_program.key),
        &[
            associated_token_program.clone(),
            payer.clone(),
            owner.clone(),
            token_account.clone(),
            mint.clone(),
            system_program.clone(),
            token_program.clone(),
        ],
    )?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
    freeze(freeze_authority, mint, token_account, token_program, &seeds)
}

pub fn process_transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let Transfer {
        src_account,
        dst_account,
        mint,
        owner,
        upstream_authority,
        freeze_authority,
        token_program,
    } = Transfer::load(accounts)?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
    thaw(freeze_authority, mint, src_account, token_program, &seeds)?;
    thaw(freeze_authority, mint, dst_account, token_program, &seeds)?;
    transfer(src_account, dst_account, owner, token_program, amount)?;
    freeze(freeze_authority, mint, dst_account, token_program, &seeds)?;
    freeze(freeze_authority, mint, src_account, token_program, &seeds)
}

pub fn process_mint_to(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let Mint {
        mint,
        token_account,
        upstream_authority,
        freeze_and_mint_authority: authority,
        token_program,
    } = Mint::load(accounts)?;
    let authority_seeds = get_authority_seeds_checked(upstream_authority.key, authority.key)?;
    thaw(
        authority,
        mint,
        token_account,
        token_program,
        &authority_seeds,
    )?;
    mint_to(
        mint,
        token_account,
        authority,
        token_program,
        amount,
        &authority_seeds,
    )?;
    freeze(
        authority,
        mint,
        token_account,
        token_program,
        &authority_seeds,
    )
}

pub fn process_burn(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let Burn {
        mint,
        token_account,
        owner,
        upstream_authority,
        freeze_authority,
        token_program,
    } = Burn::load(accounts)?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
    thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
    burn(mint, token_account, owner, token_program, amount)?;
    freeze(freeze_authority, mint, token_account, token_program, &seeds)
}

pub fn process_close(accounts: &[AccountInfo]) -> ProgramResult {
    let Close {
        token_account,
        dst_account,
        mint,
        owner,
        upstream_authority,
        freeze_authority,
        token_program,
    } = Close::load(accounts)?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
    thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
    close(token_account, dst_account, owner, token_program)
}

pub fn process_approve(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let Approve {
        mint,
        token_account,
        owner,
        upstream_authority,
        delegate,
        freeze_authority,
        token_program,
    } = Approve::load(accounts)?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
    thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
    approve(token_account, owner, delegate, token_program, amount)?;
    freeze(freeze_authority, mint, token_account, token_program, &seeds)
}

pub fn process_revoke(accounts: &[AccountInfo]) -> ProgramResult {
    let Revoke {
        mint,
        token_account,
        owner,
        upstream_authority,
        freeze_authority,
        token_program,
    } = Revoke::load(accounts)?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, freeze_authority.key)?;
    thaw(freeze_authority, mint, token_account, token_program, &seeds)?;
    revoke(token_account, owner, token_program)?;
    freeze(freeze_authority, mint, token_account, token_program, &seeds)
}

pub fn process_migrate_authority(accounts: &[AccountInfo]) -> ProgramResult {
    let MigrateAuthority {
        mint,
        upstream_authority,
        authority,
        new_freeze_authority,
        new_mint_authority,
        token_program,
    } = MigrateAuthority::load(accounts)?;
    let seeds = get_authority_seeds_checked(upstream_authority.key, authority.key)?;
    set_authority(
        mint,
        new_mint_authority.key,
        AuthorityType::MintTokens,
        authority,
        token_program,
        &seeds,
    )?;
    set_authority(
        mint,
        new_freeze_authority.key,
        AuthorityType::FreezeAccount,
        authority,
        token_program,
        &seeds,
    )
}