hydra/utils/
mod.rs

1pub mod logic;
2pub mod validation;
3
4use crate::error::HydraError;
5use crate::state::{FanoutMembershipMintVoucher, FanoutMint, FANOUT_MINT_MEMBERSHIP_VOUCHER_SIZE};
6use crate::utils::validation::{assert_derivation, assert_owned_by};
7use anchor_lang::prelude::*;
8use anchor_lang::solana_program::program::invoke_signed;
9use anchor_lang::solana_program::system_instruction;
10use anchor_spl::token::TokenAccount;
11use std::convert::TryInto;
12
13pub fn create_or_allocate_account_raw<'a>(
14    program_id: Pubkey,
15    new_account_info: &AccountInfo<'a>,
16    rent_sysvar_info: &AccountInfo<'a>,
17    system_program_info: &AccountInfo<'a>,
18    payer_info: &AccountInfo<'a>,
19    size: usize,
20    signer_seeds: &[&[u8]],
21    new_acct_seeds: &[&[u8]],
22) -> Result<()> {
23    let rent = &Rent::from_account_info(rent_sysvar_info)?;
24    let required_lamports = rent
25        .minimum_balance(size)
26        .max(1)
27        .saturating_sub(new_account_info.lamports());
28    if required_lamports > 0 {
29        let seeds: &[&[&[u8]]];
30        let as_arr = [signer_seeds];
31
32        if signer_seeds.len() > 0 {
33            seeds = &as_arr;
34        } else {
35            seeds = &[];
36        }
37        invoke_signed(
38            &system_instruction::transfer(&payer_info.key, new_account_info.key, required_lamports),
39            &[
40                payer_info.clone(),
41                new_account_info.clone(),
42                system_program_info.clone(),
43            ],
44            seeds,
45        )?;
46    }
47    let accounts = &[new_account_info.clone(), system_program_info.clone()];
48    invoke_signed(
49        &system_instruction::allocate(new_account_info.key, size.try_into().unwrap()),
50        accounts,
51        &[&new_acct_seeds],
52    )?;
53    invoke_signed(
54        &system_instruction::assign(new_account_info.key, &program_id),
55        accounts,
56        &[&new_acct_seeds],
57    )?;
58    Ok(())
59}
60
61pub fn parse_fanout_mint(
62    fanout_for_mint: &mut UncheckedAccount,
63    fanout: &Pubkey,
64    fanout_mint: &Pubkey,
65) -> Result<FanoutMint> {
66    let account_info = fanout_for_mint.to_account_info();
67    let fanout_mint_bump = assert_derivation(
68        &crate::ID,
69        &account_info,
70        &[b"fanout-config", fanout.as_ref(), fanout_mint.as_ref()],
71        Some(HydraError::InvalidFanoutForMint.into()),
72    )?;
73    let fanout_mint_data: &mut [u8] = &mut fanout_for_mint.try_borrow_mut_data()?;
74    let fanout_for_mint_object: FanoutMint =
75        FanoutMint::try_deserialize(&mut fanout_mint_data.as_ref())?;
76    if fanout_mint_bump != fanout_for_mint_object.bump_seed {
77        msg!("Invalid Fanout For Mint");
78        return Err(HydraError::InvalidFanoutForMint.into());
79    }
80    Ok(fanout_for_mint_object)
81}
82
83pub fn parse_token_account(
84    account: &AccountInfo,
85    owner: &Pubkey,
86) -> Result<TokenAccount> {
87    let ref_data = account.try_borrow_data()?;
88    let mut account_data: &[u8] = &ref_data;
89    let account_object = TokenAccount::try_deserialize(&mut account_data)?;
90    if &account_object.owner != owner {
91        msg!("Token Account has wrong owner");
92        return Err(HydraError::IncorrectOwner.into());
93    }
94    Ok(account_object)
95}
96
97pub fn parse_mint_membership_voucher<'info>(
98    fanout_for_mint_membership_voucher: &mut UncheckedAccount<'info>,
99    rent: &Sysvar<'info, anchor_lang::prelude::Rent>,
100    system_program: &Program<'info, System>,
101    payer: &anchor_lang::prelude::AccountInfo<'info>,
102    membership_key: &Pubkey,
103    fanout_for_mint: &Pubkey,
104    fanout_mint: &Pubkey,
105    fanout: &Pubkey,
106) -> Result<FanoutMembershipMintVoucher> {
107    let account_info = fanout_for_mint_membership_voucher.to_account_info();
108    let mint_membership_voucher_bump = assert_derivation(
109        &crate::ID,
110        &account_info,
111        &[
112            b"fanout-membership",
113            fanout_for_mint.as_ref(),
114            membership_key.as_ref(),
115            fanout_mint.as_ref(),
116        ],
117        Some(HydraError::InvalidMembershipVoucher.into()),
118    )?;
119    let mint_voucher_empty = fanout_for_mint_membership_voucher.data_is_empty();
120
121    Ok(if mint_voucher_empty {
122        create_or_allocate_account_raw(
123            crate::ID,
124            &account_info,
125            &rent.to_account_info(),
126            &system_program,
127            payer,
128            FANOUT_MINT_MEMBERSHIP_VOUCHER_SIZE,
129            &[],
130            &[
131                b"fanout-membership",
132                &fanout_for_mint.as_ref(),
133                &membership_key.as_ref(),
134                &fanout_mint.as_ref(),
135                &[mint_membership_voucher_bump],
136            ],
137        )?;
138        FanoutMembershipMintVoucher {
139            fanout: *fanout,
140            fanout_mint: *fanout_mint,
141            last_inflow: 0,
142            bump_seed: mint_membership_voucher_bump,
143        }
144    } else {
145        let mut membership_data: &[u8] =
146            &fanout_for_mint_membership_voucher.try_borrow_mut_data()?;
147        assert_owned_by(&fanout_for_mint_membership_voucher, &crate::ID)?;
148        let membership = FanoutMembershipMintVoucher::try_deserialize(&mut membership_data)?;
149        if membership.bump_seed != mint_membership_voucher_bump {
150            msg!("Mint Membership Bump Doesnt match");
151            return Err(HydraError::InvalidMembershipVoucher.into());
152        }
153        membership
154    })
155}