use crate::{errors::ErrorCode, structs::*, utils::*};
use anchor_lang::{prelude::*, solana_program};
use anchor_spl::token::{self, Mint, MintTo, Token, TokenAccount};
#[derive(Accounts)]
pub struct CreateAsset<'info> {
#[account(has_one = authority)]
pub asset_manager: Account<'info, AssetManager>,
#[account(
init,
payer = payer,
mint::decimals = 0,
mint::authority = asset_manager,
mint::freeze_authority = asset_manager,
)]
pub mint: Account<'info, Mint>,
#[account(mut)]
pub metadata: AccountInfo<'info>,
#[account(
init, payer = payer,
space = DESCRIMINATOR_SIZE + AssetManager::LEN + EXTRA_SIZE,
seeds = [
b"asset".as_ref(),
mint.key().as_ref(),
],
bump,
)]
pub asset: Account<'info, Asset>,
pub authority: Signer<'info>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(address = token::ID)]
pub token_program: Program<'info, Token>,
pub token_metadata_program: UncheckedAccount<'info>,
pub rent: Sysvar<'info, Rent>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq)]
pub struct CreateAssetArgs {
pub candy_guard: Option<Pubkey>,
pub name: String,
pub symbol: String,
pub uri: String,
}
pub fn create_asset(ctx: Context<CreateAsset>, args: CreateAssetArgs) -> Result<()> {
let asset_manager = &mut ctx.accounts.asset_manager;
let asset = &mut ctx.accounts.asset;
asset.bump = ctx.bumps["asset"];
asset.manager = asset_manager.key();
asset.candy_guard = args.candy_guard;
asset.mint = ctx.accounts.mint.key();
asset.items_redeemed = 0;
asset.uri = args.uri;
let asset_manager_seeds = &[
b"asset_manager".as_ref(),
asset_manager.key.as_ref(),
&[asset_manager.bump],
];
let asset_manager_signer = &[&asset_manager_seeds[..]];
let create_metadata = mpl_token_metadata::instruction::create_metadata_accounts_v3(
ctx.accounts.token_metadata_program.key(),
ctx.accounts.metadata.key(),
asset.mint,
asset_manager.key(),
ctx.accounts.payer.key(),
asset_manager.key(),
args.name.clone(),
args.symbol.clone(),
asset.uri.clone(),
None,
0,
false,
true,
None,
None,
None,
);
solana_program::program::invoke_signed(
&create_metadata,
&[
ctx.accounts.metadata.clone(),
ctx.accounts.mint.to_account_info(),
asset_manager.to_account_info(),
ctx.accounts.payer.to_account_info(),
asset_manager.to_account_info(),
ctx.accounts.system_program.to_account_info(),
ctx.accounts.rent.to_account_info(),
],
asset_manager_signer,
)?;
Ok(())
}
#[derive(Accounts)]
pub struct MintAsset<'info> {
#[account()]
pub asset_manager: Account<'info, AssetManager>,
#[account(has_one = mint)]
pub asset: Account<'info, Asset>,
#[account(mut)]
pub mint: Account<'info, Mint>,
#[account(mut, has_one = mint, constraint = token_account.owner == wallet.key())]
pub token_account: Account<'info, TokenAccount>,
#[account()]
pub candy_guard: Option<AccountInfo<'info>>,
#[account(mut)]
pub wallet: Signer<'info>,
#[account(address = token::ID)]
pub token_program: Program<'info, Token>,
}
pub fn mint_asset(ctx: Context<MintAsset>, amount: u64) -> Result<()> {
let asset_manager = &ctx.accounts.asset_manager;
let asset = &mut ctx.accounts.asset;
let candy_guard = &ctx.accounts.candy_guard;
if let Some(candy_guard) = candy_guard {
if !candy_guard.is_signer || candy_guard.key() != asset.candy_guard.unwrap() {
return Err(ErrorCode::UnauthorizedMint.into());
}
} else if ctx.accounts.wallet.key() != asset_manager.authority {
return Err(ErrorCode::UnauthorizedMint.into());
}
let asset_manager_seeds = &[
b"asset_manager".as_ref(),
asset_manager.key.as_ref(),
&[asset_manager.bump],
];
let asset_manager_signer = &[&asset_manager_seeds[..]];
token::mint_to(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.asset_manager.to_account_info(),
},
asset_manager_signer,
),
amount,
)?;
Ok(())
}