hydra_wallet 0.2.3

Collective account pooling, fan out wallet, dao treasury, all of the things you need to FAN OUT
Documentation
use crate::state::{Fanout, FanoutMembershipVoucher, HOLDING_ACCOUNT_SIZE};
use crate::utils::logic::calculation::*;
use crate::utils::logic::transfer::{transfer_from_mint_holding, transfer_native};
use crate::utils::parse_fanout_mint;
use crate::utils::validation::*;
use crate::utils::*;
use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, Token};

pub fn distribute_native<'info>(
    holding_account: &mut UncheckedAccount<'info>,
    fanout: &mut Account<'info, Fanout>,
    membership_voucher: &mut Account<'info, FanoutMembershipVoucher>,
    member: UncheckedAccount<'info>,
    rent: Sysvar<'info, anchor_lang::prelude::Rent>,
) -> Result<()> {
    let total_shares = fanout.total_shares as u64;
    if holding_account.key() != fanout.account_key {
        return Err(HydraError::InvalidHoldingAccount.into());
    }
    let current_snapshot = holding_account.lamports();
    let current_snapshot_less_min =
        current_lamports(&rent, HOLDING_ACCOUNT_SIZE, current_snapshot)?;
    update_inflow(fanout, current_snapshot_less_min)?;
    let inflow_diff = calculate_inflow_change(fanout.total_inflow, membership_voucher.last_inflow)?;
    let shares = membership_voucher.shares as u64;
    let dif_dist = calculate_dist_amount(shares, inflow_diff, total_shares)?;
    update_snapshot(fanout, membership_voucher, dif_dist)?;
    membership_voucher.total_inflow = membership_voucher
        .total_inflow
        .checked_add(dif_dist)
        .ok_or(HydraError::NumericalOverflow)?;
    transfer_native(
        holding_account.to_account_info(),
        member.to_account_info(),
        current_snapshot,
        dif_dist,
    )
}

pub fn distribute_mint<'info>(
    fanout_mint: Account<'info, Mint>,
    fanout_for_mint: &mut UncheckedAccount<'info>,
    fanout_for_mint_membership_voucher: &mut UncheckedAccount<'info>,
    fanout_mint_member_token_account: &mut UncheckedAccount<'info>,
    holding_account: &mut UncheckedAccount<'info>,
    fanout: &mut Account<'info, Fanout>,
    membership_voucher: &mut Account<'info, FanoutMembershipVoucher>,
    rent: Sysvar<'info, anchor_lang::prelude::Rent>,
    system_program: Program<'info, System>,
    token_program: Program<'info, Token>,
    payer: AccountInfo<'info>,
    member: UncheckedAccount<'info>,
    membership_key: &Pubkey,
) -> Result<()> {
    msg!("Distribute For Mint");
    let mint = &fanout_mint;
    let fanout_for_mint_membership_voucher_unchecked = fanout_for_mint_membership_voucher;
    let fanout_mint_member_token_account_info = fanout_mint_member_token_account.to_account_info();
    let fanout_for_mint = fanout_for_mint;
    let total_shares = fanout.total_shares as u64;
    assert_owned_by(&fanout_for_mint, &crate::ID)?;
    assert_owned_by(&fanout_mint_member_token_account_info, &Token::id())?;
    assert_owned_by(holding_account, &anchor_spl::token::Token::id())?;
    assert_ata(
        &holding_account.to_account_info(),
        &fanout.key(),
        &fanout_mint.key(),
        Some(HydraError::HoldingAccountMustBeAnATA.into()),
    )?;
    let fanout_for_mint_object =
        &mut parse_fanout_mint(fanout_for_mint, &fanout.key(), &mint.key())?;
    if holding_account.key() != fanout_for_mint_object.token_account {
        return Err(HydraError::InvalidHoldingAccount.into());
    }
    if fanout_for_mint_object.mint != mint.to_account_info().key() {
        return Err(HydraError::MintDoesNotMatch.into());
    }
    let fanout_for_mint_membership_voucher = &mut parse_mint_membership_voucher(
        fanout_for_mint_membership_voucher_unchecked,
        &rent,
        &system_program,
        &payer.to_account_info(),
        membership_key,
        &fanout_for_mint.key(),
        &mint.key(),
        &fanout.key(),
    )?;
    let holding_account_ata = parse_token_account(holding_account, &fanout.key())?;
    parse_token_account(&fanout_mint_member_token_account_info, &member.key())?;

    let current_snapshot = holding_account_ata.amount;
    update_inflow_for_mint(fanout, fanout_for_mint_object, current_snapshot)?;
    let inflow_diff = calculate_inflow_change(
        fanout_for_mint_object.total_inflow,
        fanout_for_mint_membership_voucher.last_inflow,
    )?;
    let shares = membership_voucher.shares as u64;
    let dif_dist = calculate_dist_amount(shares, inflow_diff, total_shares)?;
    update_snapshot_for_mint(
        fanout_for_mint_object,
        fanout_for_mint_membership_voucher,
        dif_dist,
    )?;

    let mut fanout_for_mint_membership_voucher_data: &mut [u8] =
        &mut fanout_for_mint_membership_voucher_unchecked.try_borrow_mut_data()?;
    let mut fanout_for_mint_data: &mut [u8] = &mut fanout_for_mint.try_borrow_mut_data()?;

    fanout_for_mint_membership_voucher
        .try_serialize(&mut fanout_for_mint_membership_voucher_data)?;
    fanout_for_mint_object.try_serialize(&mut fanout_for_mint_data)?;
    transfer_from_mint_holding(
        fanout,
        fanout.to_account_info(),
        token_program.to_account_info(),
        holding_account.to_account_info(),
        fanout_mint_member_token_account_info,
        dif_dist,
    )
}