use crate::*;
pub fn handler(ctx: Context<ClaimRewardsV2>) -> Result<()> {
let miner = &mut ctx.accounts.claim.miner;
let now = Clock::get()?.unix_timestamp;
let quarry = &mut ctx.accounts.claim.quarry;
quarry.update_rewards_and_miner(miner, &ctx.accounts.claim.rewarder, now)?;
ctx.accounts.calculate_and_claim_rewards()?;
Ok(())
}
impl<'info> ClaimRewardsV2<'info> {
pub fn calculate_and_claim_rewards(&mut self) -> Result<()> {
let miner = &mut self.claim.miner;
let amount_claimable = miner.rewards_earned;
if amount_claimable == 0 {
return Ok(());
}
let max_claim_fee_millibps = self.claim.rewarder.max_claim_fee_millibps;
invariant!(
max_claim_fee_millibps < MAX_BPS * DEFAULT_CLAIM_FEE_MILLIBPS,
InvalidMaxClaimFee
);
let max_claim_fee = unwrap_int!(::u128::mul_div_u64(
amount_claimable,
max_claim_fee_millibps,
MAX_BPS * DEFAULT_CLAIM_FEE_MILLIBPS
));
let amount_claimable_minus_fees = unwrap_int!(amount_claimable.checked_sub(max_claim_fee));
miner.rewards_earned = 0;
self.mint_claimed_tokens(amount_claimable_minus_fees)?;
self.mint_fees(max_claim_fee)?;
let now = Clock::get()?.unix_timestamp;
emit!(ClaimEvent {
authority: self.claim.authority.key(),
staked_token: self.claim.quarry.token_mint_key,
timestamp: now,
rewards_token: self.rewards_token_mint.key(),
amount: amount_claimable_minus_fees,
fees: max_claim_fee,
});
Ok(())
}
fn mint_claimed_tokens(&self, amount_claimable_minus_fees: u64) -> Result<()> {
self.perform_mint(&self.rewards_token_account, amount_claimable_minus_fees)
}
fn mint_fees(&self, claim_fee: u64) -> Result<()> {
self.perform_mint(&self.claim_fee_token_account, claim_fee)
}
fn create_perform_mint_accounts(
&self,
destination: &Account<'info, TokenAccount>,
) -> quarry_mint_wrapper::cpi::accounts::PerformMint<'info> {
quarry_mint_wrapper::cpi::accounts::PerformMint {
mint_wrapper: self.mint_wrapper.to_account_info(),
minter_authority: self.claim.rewarder.to_account_info(),
token_mint: self.rewards_token_mint.to_account_info(),
destination: destination.to_account_info(),
minter: self.minter.to_account_info(),
token_program: self.claim.token_program.to_account_info(),
}
}
fn perform_mint(&self, destination: &Account<'info, TokenAccount>, amount: u64) -> Result<()> {
let claim_mint_accounts = self.create_perform_mint_accounts(destination);
let seeds = gen_rewarder_signer_seeds!(self.claim.rewarder);
let signer_seeds = &[&seeds[..]];
quarry_mint_wrapper::cpi::perform_mint(
CpiContext::new_with_signer(
self.mint_wrapper_program.to_account_info(),
claim_mint_accounts,
signer_seeds,
),
amount,
)
}
}
#[derive(Accounts)]
pub struct ClaimRewardsV2<'info> {
#[account(mut)]
pub mint_wrapper: Box<Account<'info, quarry_mint_wrapper::MintWrapper>>,
pub mint_wrapper_program: Program<'info, quarry_mint_wrapper::program::QuarryMintWrapper>,
#[account(mut)]
pub minter: Box<Account<'info, quarry_mint_wrapper::Minter>>,
#[account(mut)]
pub rewards_token_mint: Box<Account<'info, Mint>>,
#[account(mut)]
pub rewards_token_account: Box<Account<'info, TokenAccount>>,
#[account(mut)]
pub claim_fee_token_account: Box<Account<'info, TokenAccount>>,
pub claim: UserClaimV2<'info>,
}
#[derive(Accounts, Clone)]
pub struct UserClaimV2<'info> {
pub authority: Signer<'info>,
#[account(mut)]
pub miner: Account<'info, Miner>,
#[account(mut)]
pub quarry: Account<'info, Quarry>,
pub token_program: Program<'info, Token>,
pub rewarder: Account<'info, Rewarder>,
}
impl<'info> Validate<'info> for ClaimRewardsV2<'info> {
fn validate(&self) -> Result<()> {
self.claim.validate()?;
self.claim.rewarder.assert_not_paused()?;
assert_keys_eq!(self.mint_wrapper, self.claim.rewarder.mint_wrapper);
assert_keys_eq!(self.mint_wrapper.token_mint, self.rewards_token_mint);
assert_keys_eq!(self.minter.mint_wrapper, self.mint_wrapper);
assert_keys_eq!(self.minter.minter_authority, self.claim.rewarder);
assert_keys_eq!(
self.rewards_token_mint,
self.claim.rewarder.rewards_token_mint
);
assert_keys_eq!(
self.rewards_token_mint.mint_authority.unwrap(),
self.mint_wrapper
);
assert_keys_eq!(self.rewards_token_account.mint, self.rewards_token_mint);
assert_keys_eq!(
self.claim_fee_token_account,
self.claim.rewarder.claim_fee_token_account
);
assert_keys_eq!(self.claim_fee_token_account.mint, self.rewards_token_mint);
Ok(())
}
}
impl<'info> Validate<'info> for UserClaimV2<'info> {
fn validate(&self) -> Result<()> {
invariant!(!self.rewarder.is_paused, Paused);
invariant!(self.authority.is_signer, Unauthorized);
assert_keys_eq!(self.authority, self.miner.authority);
assert_keys_eq!(self.miner.quarry, self.quarry);
assert_keys_eq!(self.quarry.rewarder, self.rewarder);
Ok(())
}
}
#[event]
pub struct ClaimEvent {
#[index]
pub authority: Pubkey,
#[index]
pub staked_token: Pubkey,
pub rewards_token: Pubkey,
pub amount: u64,
pub fees: u64,
pub timestamp: i64,
}