#![deny(rustdoc::all)]
#![allow(rustdoc::missing_doc_code_examples)]
#![allow(deprecated)]
#[macro_use]
mod macros;
use anchor_lang::prelude::*;
use anchor_spl::token::Token;
use anchor_spl::token::{self, Mint, TokenAccount};
use vipers::prelude::*;
mod account_validators;
mod instructions;
mod state;
use instructions::*;
pub use state::*;
declare_id!("QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV");
#[cfg(not(feature = "no-entrypoint"))]
solana_security_txt::security_txt! {
name: "Quarry Mint Wrapper",
project_url: "https://quarry.so",
contacts: "email:team@quarry.so",
policy: "https://github.com/QuarryProtocol/quarry/blob/master/SECURITY.md",
source_code: "https://github.com/QuarryProtocol/quarry",
auditors: "Quantstamp"
}
#[program]
pub mod quarry_mint_wrapper {
use super::*;
#[deprecated(since = "5.0.0", note = "Use `new_wrapper_v2` instead.")]
#[access_control(ctx.accounts.validate())]
pub fn new_wrapper(ctx: Context<NewWrapper>, _bump: u8, hard_cap: u64) -> Result<()> {
instructions::new_wrapper::handler(ctx, hard_cap)
}
#[access_control(ctx.accounts.validate())]
pub fn new_wrapper_v2(ctx: Context<NewWrapper>, hard_cap: u64) -> Result<()> {
instructions::new_wrapper::handler(ctx, hard_cap)
}
#[access_control(ctx.accounts.validate())]
pub fn transfer_admin(ctx: Context<TransferAdmin>) -> Result<()> {
let mint_wrapper = &mut ctx.accounts.mint_wrapper;
mint_wrapper.pending_admin = ctx.accounts.next_admin.key();
emit!(MintWrapperAdminProposeEvent {
mint_wrapper: mint_wrapper.key(),
current_admin: mint_wrapper.admin,
pending_admin: mint_wrapper.pending_admin,
});
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn accept_admin(ctx: Context<AcceptAdmin>) -> Result<()> {
let mint_wrapper = &mut ctx.accounts.mint_wrapper;
let previous_admin = mint_wrapper.admin;
mint_wrapper.admin = ctx.accounts.pending_admin.key();
mint_wrapper.pending_admin = Pubkey::default();
emit!(MintWrapperAdminUpdateEvent {
mint_wrapper: mint_wrapper.key(),
previous_admin,
admin: mint_wrapper.admin,
});
Ok(())
}
#[deprecated(since = "5.0.0", note = "Use `new_minter_v2` instead.")]
#[access_control(ctx.accounts.validate())]
pub fn new_minter(ctx: Context<NewMinter>, _bump: u8) -> Result<()> {
instructions::new_minter::handler(ctx)
}
#[access_control(ctx.accounts.validate())]
pub fn new_minter_v2(ctx: Context<NewMinter>) -> Result<()> {
instructions::new_minter::handler(ctx)
}
#[access_control(ctx.accounts.validate())]
pub fn minter_update(ctx: Context<MinterUpdate>, allowance: u64) -> Result<()> {
let minter = &mut ctx.accounts.minter;
let previous_allowance = minter.allowance;
minter.allowance = allowance;
let mint_wrapper = &mut ctx.accounts.auth.mint_wrapper;
mint_wrapper.total_allowance = unwrap_int!(mint_wrapper
.total_allowance
.checked_add(allowance)
.and_then(|v| v.checked_sub(previous_allowance)));
emit!(MinterAllowanceUpdateEvent {
mint_wrapper: minter.mint_wrapper,
minter: minter.key(),
previous_allowance,
allowance: minter.allowance,
});
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn perform_mint(ctx: Context<PerformMint>, amount: u64) -> Result<()> {
let mint_wrapper = &ctx.accounts.mint_wrapper;
let minter = &mut ctx.accounts.minter;
invariant!(minter.allowance >= amount, MinterAllowanceExceeded);
let new_supply = unwrap_int!(ctx.accounts.token_mint.supply.checked_add(amount));
invariant!(new_supply <= mint_wrapper.hard_cap, HardcapExceeded);
minter.allowance = unwrap_int!(minter.allowance.checked_sub(amount));
minter.total_minted = unwrap_int!(minter.total_minted.checked_add(amount));
let seeds = gen_wrapper_signer_seeds!(mint_wrapper);
let proxy_signer = &[&seeds[..]];
let cpi_ctx = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::MintTo {
mint: ctx.accounts.token_mint.to_account_info(),
to: ctx.accounts.destination.to_account_info(),
authority: ctx.accounts.mint_wrapper.to_account_info(),
},
proxy_signer,
);
token::mint_to(cpi_ctx, amount)?;
let mint_wrapper = &mut ctx.accounts.mint_wrapper;
mint_wrapper.total_allowance =
unwrap_int!(mint_wrapper.total_allowance.checked_sub(amount));
mint_wrapper.total_minted = unwrap_int!(mint_wrapper.total_minted.checked_add(amount));
ctx.accounts.token_mint.reload()?;
invariant!(new_supply == ctx.accounts.token_mint.supply, Unauthorized);
emit!(MinterMintEvent {
mint_wrapper: mint_wrapper.key(),
minter: minter.key(),
amount,
destination: ctx.accounts.destination.key(),
});
Ok(())
}
}
#[derive(Accounts)]
pub struct MinterUpdate<'info> {
pub auth: OnlyAdmin<'info>,
#[account(mut)]
pub minter: Account<'info, Minter>,
}
#[derive(Accounts)]
pub struct TransferAdmin<'info> {
#[account(mut)]
pub mint_wrapper: Account<'info, MintWrapper>,
pub admin: Signer<'info>,
pub next_admin: UncheckedAccount<'info>,
}
#[derive(Accounts)]
pub struct AcceptAdmin<'info> {
#[account(mut)]
pub mint_wrapper: Account<'info, MintWrapper>,
pub pending_admin: Signer<'info>,
}
#[derive(Accounts, Clone)]
pub struct PerformMint<'info> {
#[account(mut)]
pub mint_wrapper: Account<'info, MintWrapper>,
pub minter_authority: Signer<'info>,
#[account(mut)]
pub token_mint: Account<'info, Mint>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
#[account(mut)]
pub minter: Account<'info, Minter>,
pub token_program: Program<'info, Token>,
}
#[derive(Accounts)]
pub struct OnlyAdmin<'info> {
#[account(mut, has_one = admin @ ErrorCode::Unauthorized)]
pub mint_wrapper: Account<'info, MintWrapper>,
pub admin: Signer<'info>,
}
#[event]
pub struct NewMintWrapperEvent {
#[index]
pub mint_wrapper: Pubkey,
pub hard_cap: u64,
pub admin: Pubkey,
pub token_mint: Pubkey,
}
#[event]
pub struct MintWrapperAdminProposeEvent {
#[index]
pub mint_wrapper: Pubkey,
pub current_admin: Pubkey,
pub pending_admin: Pubkey,
}
#[event]
pub struct MintWrapperAdminUpdateEvent {
#[index]
pub mint_wrapper: Pubkey,
pub previous_admin: Pubkey,
pub admin: Pubkey,
}
#[event]
pub struct NewMinterEvent {
#[index]
pub mint_wrapper: Pubkey,
#[index]
pub minter: Pubkey,
pub index: u64,
pub minter_authority: Pubkey,
}
#[event]
pub struct MinterAllowanceUpdateEvent {
#[index]
pub mint_wrapper: Pubkey,
#[index]
pub minter: Pubkey,
pub previous_allowance: u64,
pub allowance: u64,
}
#[event]
pub struct MinterMintEvent {
#[index]
pub mint_wrapper: Pubkey,
#[index]
pub minter: Pubkey,
pub amount: u64,
pub destination: Pubkey,
}
#[error_code]
pub enum ErrorCode {
#[msg("You are not authorized to perform this action.")]
Unauthorized,
#[msg("Cannot mint over hard cap.")]
HardcapExceeded,
#[msg("Minter allowance exceeded.")]
MinterAllowanceExceeded,
}