#![deny(rustdoc::all)]
#![allow(rustdoc::missing_doc_code_examples)]
use anchor_lang::prelude::*;
use anchor_spl::token::Token;
use anchor_spl::token::{Mint, TokenAccount};
use vipers::invariant;
use vipers::unwrap_int;
use vipers::validate::Validate;
mod account_validators;
mod macros;
mod redeem_cpi;
declare_id!("QRDxhMw1P2NEfiw5mYXG79bwfgHTdasY2xNP76XSea9");
#[program]
pub mod quarry_redeemer {
use super::*;
#[access_control(ctx.accounts.validate())]
pub fn create_redeemer(ctx: Context<CreateRedeemer>, bump: u8) -> ProgramResult {
let redeemer = &mut ctx.accounts.redeemer;
redeemer.iou_mint = ctx.accounts.iou_mint.key();
redeemer.redemption_mint = ctx.accounts.redemption_mint.key();
redeemer.bump = bump;
redeemer.total_tokens_redeemed = 0;
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn redeem_tokens(ctx: Context<RedeemTokens>, amount: u64) -> ProgramResult {
invariant!(
amount <= ctx.accounts.iou_source.amount,
"insufficient iou_source balance"
);
invariant!(
amount <= ctx.accounts.redemption_vault.amount,
"insufficient redemption_vault balance"
);
ctx.accounts.burn_iou_tokens(amount)?;
ctx.accounts.transfer_redemption_tokens(amount)?;
let redeemer = &mut ctx.accounts.redeemer;
redeemer.total_tokens_redeemed =
unwrap_int!(redeemer.total_tokens_redeemed.checked_add(amount));
let redeemer = &ctx.accounts.redeemer;
emit!(RedeemTokensEvent {
user: ctx.accounts.source_authority.key(),
iou_mint: redeemer.iou_mint,
redemption_mint: redeemer.redemption_mint,
amount,
timestamp: Clock::get()?.unix_timestamp
});
Ok(())
}
pub fn redeem_all_tokens(ctx: Context<RedeemTokens>) -> ProgramResult {
let amount = ctx.accounts.iou_source.amount;
redeem_tokens(ctx, amount)
}
}
#[account]
#[derive(Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Redeemer {
pub iou_mint: Pubkey,
pub redemption_mint: Pubkey,
pub bump: u8,
pub total_tokens_redeemed: u64,
}
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct CreateRedeemer<'info> {
#[account(
init,
seeds = [
b"Redeemer".as_ref(),
iou_mint.to_account_info().key.as_ref(),
redemption_mint.to_account_info().key.as_ref()
],
bump = bump,
payer = payer
)]
pub redeemer: Account<'info, Redeemer>,
pub iou_mint: Account<'info, Mint>,
pub redemption_mint: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct RedeemTokens<'info> {
#[account(mut)]
pub redeemer: Account<'info, Redeemer>,
pub source_authority: Signer<'info>,
#[account(mut)]
pub iou_mint: Account<'info, Mint>,
#[account(mut)]
pub iou_source: Account<'info, TokenAccount>,
#[account(mut)]
pub redemption_vault: Account<'info, TokenAccount>,
#[account(mut)]
pub redemption_destination: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}
#[event]
pub struct RedeemTokensEvent {
#[index]
pub user: Pubkey,
pub iou_mint: Pubkey,
pub redemption_mint: Pubkey,
pub amount: u64,
pub timestamp: i64,
}
#[error]
pub enum ErrorCode {
#[msg("Unauthorized.")]
Unauthorized,
}