strategy-vaults 0.0.1

Farm Vaults
Documentation
use crate::*;
use anchor_spl::token::{Mint, TokenAccount};
use vipers::{assert_is_zero_token_account, assert_keys_eq, invariant, AsKeyRef, Validate};

// Ground rules:
// No information here should have anything to do with users of this vault.
// No funding account information.
// All of this should be metadata to find a Raydium AMM and use its pool and vault.
// Nothing more, nothing less.

// Some accounts to consider adding:
// stake pool accounts.
// pool-amm-details
// amm owner, target_orders, market, lp_mint, etc.
// The Raydium AMM Pool Account

#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct CreateVault<'info> {
    /// This is a vault info account.
    #[account(
        init,
        seeds = [
            b"raydium_stake_lp_compound_vault".as_ref(),
            amm.to_account_info().key.as_ref(),
            base.key.as_ref()
        ],
        bump,
        payer = payer
    )]
    pub vault: Box<Account<'info, Vault>>,

    /// Base of the [Vault] address.
    pub base: Signer<'info>,

    /// Manager of the vault.
    pub manager: Signer<'info>,

    /// Fee payer.
    #[account(mut)]
    pub payer: Signer<'info>,

    /// [System] program.
    pub system_program: Program<'info, System>,

    /// Vault [Mint]
    #[account(mut)]
    pub vault_lp_token_mint: Box<Account<'info, Mint>>,

    // farm Mint
    pub farm_lp_mint: Box<Account<'info, Mint>>,

    /// Reward Mints
    pub farm_reward_token_a_mint: Box<Account<'info, Mint>>,
    pub farm_reward_token_b_mint: Box<Account<'info, Mint>>,

    /// The Pool custody [TokenAccount]s.
    pub pool_lp_custody_account: Box<Account<'info, TokenAccount>>,
    pub pool_token_a_custody_account: Box<Account<'info, TokenAccount>>,
    pub pool_token_b_custody_account: Box<Account<'info, TokenAccount>>,

    // Pool Id
    pub amm: Box<Account<'info, AmmInfoV4>>,
}

/// Creates a new [Vault].
pub fn handler(
    ctx: Context<CreateVault>,
    manager: Pubkey,
    serum_program_id: Pubkey,
    pool_program_id: Pubkey,
    farm_program_id: Pubkey,
    farm: Pubkey,
    farm_authority: Pubkey,
    serum_market: Pubkey,
    serum_vault_signer: Pubkey,
    amm_authority: Pubkey,
) -> Result<()> {
    // Creating the vault info account.
    msg!("Starting vault creation.");
    let vault = &mut ctx.accounts.vault;
    vault.base = ctx.accounts.base.key();
    vault.bump = *ctx.bumps.get("vault").unwrap();

    // Setting vault authority
    vault.manager = manager;

    // Setting vault token mint
    vault.vault_lp_token_mint = ctx.accounts.vault_lp_token_mint.key();

    // The only reason I am asking to pass this in is because I dont have a farm.
    // set the farm lp mint.
    vault.farm_lp_mint = ctx.accounts.farm_lp_mint.key();

    // set the farm reward mints.
    vault.farm_reward_token_a_mint = ctx.accounts.farm_reward_token_a_mint.key();
    vault.farm_reward_token_b_mint = ctx.accounts.farm_reward_token_b_mint.key();

    // Token Mints.
    // COIN - RAY
    // PC - USDC
    vault.pool_token_a_mint = *ctx.accounts.amm.coin_mint.as_key_ref();
    vault.pool_token_b_mint = *ctx.accounts.amm.pc_mint.as_key_ref();

    // Pool Lp Token Mint
    vault.pool_lp_token_mint = *ctx.accounts.amm.lp_mint.as_key_ref();

    // Setting the farm custody accounts.
    assert_keys_eq!(
        ctx.accounts.pool_lp_custody_account.mint,
        ctx.accounts.amm.lp_mint
    );
    assert_keys_eq!(
        ctx.accounts.pool_token_a_custody_account.mint,
        ctx.accounts.amm.coin_mint
    );
    assert_keys_eq!(
        ctx.accounts.pool_token_b_custody_account.mint,
        ctx.accounts.amm.pc_mint
    );
    // COIN MINT - RAY == BASE - token A
    // PC MINT - USDC == QUOTE - token B

    vault.pool_lp_custody_account = ctx.accounts.pool_lp_custody_account.key();
    vault.pool_token_a_custody_account = ctx.accounts.pool_token_a_custody_account.key();
    vault.pool_token_b_custody_account = ctx.accounts.pool_token_b_custody_account.key();

    // Raydium AMM ID
    vault.amm = ctx.accounts.amm.key();

    // Pool Information
    vault.pool_coin_token_account = *ctx.accounts.amm.token_coin.as_key_ref();
    vault.pool_pc_token_account = *ctx.accounts.amm.token_pc.as_key_ref();
    vault.pool_withdraw_queue = *ctx.accounts.amm.withdraw_queue.as_key_ref();
    vault.pool_temp_lp_token_account = *ctx.accounts.amm.token_temp_lp.as_key_ref();

    // Amm Information
    // AMM authority is not in the struct.
    vault.amm_authority = amm_authority;
    vault.amm_open_orders = *ctx.accounts.amm.open_orders.as_key_ref();
    vault.amm_target = *ctx.accounts.amm.target_orders.as_key_ref();

    // Raydium Farm ID
    vault.farm = farm;
    vault.farm_authority = farm_authority;

    vault.serum_market = serum_market;
    vault.serum_program_id = serum_program_id;
    vault.serum_vault_signer = serum_vault_signer;

    // Raydium Program Ids
    vault.pool_program_id = pool_program_id;
    vault.farm_program_id = farm_program_id;

    // Adminstrator Metadata
    vault.deposits_allowed = true;
    vault.withdraws_allowed = true;
    vault.fee_millibps = 0;
    vault.external_fee_millibps = 0;

    // Crank metadata
    vault.crank_time = clock::get_time_as_u64()?;
    vault.crank_step = 0;

    // Vault Information
    vault.token_a_added = 0;
    vault.token_b_added = 0;
    vault.token_a_removed = 0;
    vault.token_b_removed = 0;
    vault.token_a_rewards = 0;
    vault.token_b_rewards = 0;

    msg!("Finished creating vaults.");

    emit!(VaultCreateEvent {
        vault: vault.key(),
        manager,
    });
    Ok(())
}

impl<'info> Validate<'info> for CreateVault<'info> {
    fn validate(&self) -> Result<()> {
        assert_is_zero_token_account!(self.pool_lp_custody_account);
        assert_is_zero_token_account!(self.pool_token_a_custody_account);
        assert_is_zero_token_account!(self.pool_token_b_custody_account);

        invariant!(self.vault_lp_token_mint.supply == 0);

        // make sure mint authority is the funding pool
        assert_keys_eq!(self.vault_lp_token_mint.mint_authority.unwrap(), self.vault,);

        assert_keys_eq!(self.pool_lp_custody_account.owner, self.vault);
        assert_keys_eq!(self.pool_token_a_custody_account.owner, self.vault,);
        assert_keys_eq!(self.pool_token_b_custody_account.owner, self.vault,);

        assert_keys_eq!(self.pool_lp_custody_account.mint, self.amm.lp_mint);
        assert_keys_eq!(self.pool_token_a_custody_account.mint, self.amm.coin_mint);
        assert_keys_eq!(self.pool_token_b_custody_account.mint, self.amm.pc_mint);

        Ok(())
    }
}