strategy-vaults 0.0.1

Farm Vaults
Documentation
use crate::*;
use anchor_spl::token::TokenAccount;
use solana_farm_sdk::program::protocol::raydium;
use vipers::{assert_is_zero_token_account, assert_keys_eq, 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)]
pub struct CreateVaultTwo<'info> {
    /// This is a vault info account.
    #[account(mut)]
    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>,

    #[account(
        init,
        seeds = [
            b"two_staker_info".as_ref(),
            base.key.as_ref(),
            vault.to_account_info().key.as_ref(),
        ],
        bump,
        payer = payer,
        space = 96
    )]
    pub stake_info_account_v4: Box<Account<'info, RaydiumUserStakeInfoV4>>,

    #[account(
        init,
        seeds = [
            b"three_staker_info_v2".as_ref(),
            base.key.as_ref(),
            vault.to_account_info().key.as_ref(),
        ],
        owner= raydium::raydium_stake_v5_dev::ID,
        bump,
        payer = payer,
        space = 96
    )]
    /// CHECK: this one is created here.
    pub stake_info_account_v5: UncheckedAccount<'info>,

    /// Farm Reward Custody Account [TokenAccount]
    pub farm_token_a_reward_custody_account: Box<Account<'info, TokenAccount>>,
    pub farm_token_b_reward_custody_account: Box<Account<'info, TokenAccount>>,

    pub farm_lp_token_account: Box<Account<'info, TokenAccount>>,
    pub farm_reward_token_a_account: Box<Account<'info, TokenAccount>>,
    pub farm_reward_token_b_account: Box<Account<'info, TokenAccount>>,

    pub serum_coin_vault_account: Box<Account<'info, TokenAccount>>,
    pub serum_pc_vault_account: Box<Account<'info, TokenAccount>>,

    // fees accounts
    pub fees_account_a: Box<Account<'info, TokenAccount>>,
    pub fees_account_b: Box<Account<'info, TokenAccount>>,

    // serum information
    /// CHECK: Raydium verifies
    pub serum_bids: UncheckedAccount<'info>,
    /// CHECK: Raydium verifies
    pub serum_asks: UncheckedAccount<'info>,
    /// CHECK: Raydium verifies
    pub serum_event_queue: UncheckedAccount<'info>,
}

/// Creates a new [Vault].
pub fn handler(ctx: Context<CreateVaultTwo>, farm_version: u64) -> Result<()> {
    // Creating the vault info account.
    msg!("Starting vault creation.");
    let vault = &mut ctx.accounts.vault;
    vault.bump2 = *ctx.bumps.get("stake_info_account_v4").unwrap();
    vault.bump3 = *ctx.bumps.get("stake_info_account_v5").unwrap();

    vault.stake_info_account_v4 = ctx.accounts.stake_info_account_v4.key();
    vault.stake_info_account_v5 = ctx.accounts.stake_info_account_v5.key();
    vault.farm_version = farm_version;

    // fee accounts for cranks
    vault.fees_account_a = ctx.accounts.fees_account_a.key();
    vault.fees_account_b = ctx.accounts.fees_account_b.key();

    // Set the reward custody accounts.
    // P2 - TODO: assert the mints are actually correct for these accounts.
    // Requires Raydium Farm Program Id to be a struct
    vault.farm_token_a_reward_custody_account =
        ctx.accounts.farm_token_a_reward_custody_account.key();
    vault.farm_token_b_reward_custody_account =
        ctx.accounts.farm_token_b_reward_custody_account.key();

    vault.farm_lp_token_account = ctx.accounts.farm_lp_token_account.key();
    vault.farm_reward_token_a_account = ctx.accounts.farm_reward_token_a_account.key();
    vault.farm_reward_token_b_account = ctx.accounts.farm_reward_token_b_account.key();

    // Serum Information
    // P2 - TODO - How does one validate this?
    vault.serum_coin_vault_account = ctx.accounts.serum_coin_vault_account.key();
    vault.serum_pc_vault_account = ctx.accounts.serum_pc_vault_account.key();

    // serum information for swaps.
    vault.serum_bids = ctx.accounts.serum_bids.key();
    vault.serum_asks = ctx.accounts.serum_asks.key();
    vault.serum_event_queue = ctx.accounts.serum_event_queue.key();

    msg!("Finished creating vaults.");

    Ok(())
}

impl<'info> Validate<'info> for CreateVaultTwo<'info> {
    fn validate(&self) -> Result<()> {
        assert_is_zero_token_account!(self.farm_token_a_reward_custody_account);
        assert_is_zero_token_account!(self.farm_token_b_reward_custody_account);
        assert_is_zero_token_account!(self.fees_account_a);
        assert_is_zero_token_account!(self.fees_account_b);

        assert_keys_eq!(self.farm_token_a_reward_custody_account.owner, self.vault,);
        assert_keys_eq!(self.farm_token_b_reward_custody_account.owner, self.vault,);

        assert_keys_eq!(self.fees_account_a.owner, self.vault);
        assert_keys_eq!(self.fees_account_b.owner, self.vault);

        assert_keys_eq!(
            self.farm_token_a_reward_custody_account.mint,
            self.vault.farm_reward_token_a_mint
        );
        assert_keys_eq!(
            self.farm_token_b_reward_custody_account.mint,
            self.vault.farm_reward_token_b_mint
        );
        assert_keys_eq!(self.farm_lp_token_account.mint, self.vault.farm_lp_mint);

        Ok(())
    }
}