use mpl_token_metadata::utils::assert_derivation;
use crate::errors::ErrorCode;
use crate::state::*;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::invoke;
use anchor_lang::solana_program::program::invoke_signed;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::associated_token::{self};
use anchor_spl::token::Mint;
use anchor_spl::token::Token;
use anchor_spl::token::{self};
use cardinal_token_manager::program::CardinalTokenManager;
use cardinal_token_manager::{self};
use mpl_token_metadata::instruction::create_metadata_accounts_v3;
use mpl_token_metadata::state::Creator;
use mpl_token_metadata::state::Metadata;
use mpl_token_metadata::{self};
use solana_program::program_pack::Pack;
use solana_program::system_instruction::create_account;
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct InitStakeMintIx {
name: String,
symbol: String,
}
#[derive(Accounts)]
#[instruction(ix: InitStakeMintIx)]
pub struct InitStakeMintCtx<'info> {
#[account(mut, constraint = stake_entry.stake_mint.is_none() @ ErrorCode::StakeMintAlreadyInitialized)]
stake_entry: Box<Account<'info, StakeEntry>>,
#[account(mut, constraint = stake_entry.pool == stake_pool.key() @ ErrorCode::InvalidStakePool)]
stake_pool: Box<Account<'info, StakePool>>,
original_mint: Box<Account<'info, Mint>>,
original_mint_metadata: AccountInfo<'info>,
#[account(mut)]
stake_mint: Signer<'info>,
#[account(mut)]
stake_mint_metadata: UncheckedAccount<'info>,
#[account(mut)]
stake_entry_stake_mint_token_account: UncheckedAccount<'info>,
#[account(mut)]
mint_manager: UncheckedAccount<'info>,
payer: Signer<'info>,
rent: Sysvar<'info, Rent>,
token_program: Program<'info, Token>,
token_manager_program: Program<'info, CardinalTokenManager>,
associated_token: Program<'info, AssociatedToken>,
#[account(address = mpl_token_metadata::id())]
token_metadata_program: UncheckedAccount<'info>,
system_program: Program<'info, System>,
}
pub fn handler(ctx: Context<InitStakeMintCtx>, ix: InitStakeMintIx) -> Result<()> {
let stake_entry = &mut ctx.accounts.stake_entry;
let stake_pool_identifier = ctx.accounts.stake_pool.identifier.to_le_bytes();
let stake_pool_seeds = &[STAKE_POOL_PREFIX.as_bytes(), stake_pool_identifier.as_ref(), &[ctx.accounts.stake_pool.bump]];
let stake_pool_signer = &[&stake_pool_seeds[..]];
stake_entry.stake_mint = Some(ctx.accounts.stake_mint.key());
invoke(
&create_account(
ctx.accounts.payer.key,
ctx.accounts.stake_mint.key,
ctx.accounts.rent.minimum_balance(spl_token::state::Mint::LEN),
spl_token::state::Mint::LEN as u64,
&spl_token::id(),
),
&[ctx.accounts.payer.to_account_info(), ctx.accounts.stake_mint.to_account_info()],
)?;
let cpi_accounts = token::InitializeMint {
mint: ctx.accounts.stake_mint.to_account_info(),
rent: ctx.accounts.rent.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
token::initialize_mint(cpi_context, 0, &ctx.accounts.stake_pool.key(), Some(&ctx.accounts.stake_pool.key()))?;
let cpi_accounts = associated_token::Create {
payer: ctx.accounts.payer.to_account_info(),
associated_token: ctx.accounts.stake_entry_stake_mint_token_account.to_account_info(),
authority: stake_entry.to_account_info(),
mint: ctx.accounts.stake_mint.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts);
associated_token::create(cpi_context)?;
let mut metadata_uri_param: String = "".to_string();
assert_derivation(
&mpl_token_metadata::id(),
&ctx.accounts.original_mint_metadata.to_account_info(),
&[
mpl_token_metadata::state::PREFIX.as_bytes(),
mpl_token_metadata::id().as_ref(),
ctx.accounts.original_mint.key().as_ref(),
],
)?;
if !ctx.accounts.original_mint_metadata.data_is_empty() {
let mint_metadata_data = ctx.accounts.original_mint_metadata.try_borrow_mut_data().expect("Failed to borrow data");
let original_mint_metadata = Metadata::deserialize(&mut mint_metadata_data.as_ref())?;
metadata_uri_param = "&uri=".to_string() + original_mint_metadata.data.uri.trim_matches(char::from(0));
}
invoke_signed(
&create_metadata_accounts_v3(
*ctx.accounts.token_metadata_program.key,
*ctx.accounts.stake_mint_metadata.key,
*ctx.accounts.stake_mint.key,
ctx.accounts.stake_pool.key(),
*ctx.accounts.payer.key,
ctx.accounts.stake_pool.key(),
ix.name,
ix.symbol,
"https://api.cardinal.so/metadata/".to_string() + &ctx.accounts.stake_mint.key().to_string() + "?text=" + &ctx.accounts.stake_pool.overlay_text + &metadata_uri_param,
Some(vec![
Creator {
address: ctx.accounts.stake_pool.key(),
verified: true,
share: 50,
},
Creator {
address: stake_entry.key(),
verified: false,
share: 50,
},
]),
1,
true,
true,
None,
None,
None,
),
&[
ctx.accounts.stake_mint_metadata.to_account_info(),
ctx.accounts.stake_mint.to_account_info(),
ctx.accounts.stake_pool.to_account_info(),
ctx.accounts.payer.to_account_info(),
ctx.accounts.stake_pool.to_account_info(),
ctx.accounts.system_program.to_account_info(),
ctx.accounts.rent.to_account_info(),
],
stake_pool_signer,
)?;
let cpi_accounts = token::MintTo {
mint: ctx.accounts.stake_mint.to_account_info(),
to: ctx.accounts.stake_entry_stake_mint_token_account.to_account_info(),
authority: ctx.accounts.stake_pool.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_context = CpiContext::new(cpi_program, cpi_accounts).with_signer(stake_pool_signer);
token::mint_to(cpi_context, 1)?;
let token_manager_program = ctx.accounts.token_manager_program.to_account_info();
let cpi_accounts = cardinal_token_manager::cpi::accounts::CreateMintManagerCtx {
mint_manager: ctx.accounts.mint_manager.to_account_info(),
mint: ctx.accounts.stake_mint.to_account_info(),
freeze_authority: ctx.accounts.stake_pool.to_account_info(),
payer: ctx.accounts.payer.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
};
let cpi_ctx = CpiContext::new(token_manager_program, cpi_accounts).with_signer(stake_pool_signer);
cardinal_token_manager::cpi::create_mint_manager(cpi_ctx)?;
Ok(())
}