use anchor_lang::prelude::*;
use anchor_spl::token::{self, Transfer};
use crate::state::{MemberInfo, InitializeWallet, AddMember, Distribute};
use crate::utils::{assert_ata, assert_ata_for_pubkey, assert_ata_for_keys};
use crate::error::TentaClesError;
pub fn initialize_wallet(ctx: Context<InitializeWallet>, name: String, total_shares: u64, bump: u8) -> Result<()> {
let wallet = &mut ctx.accounts.wallet;
let wallet_key = wallet.key(); assert_ata(
&ctx.accounts.wallet_token_account.to_account_info(),
&wallet_key,
&ctx.accounts.mint.key(),
Some(TentaClesError::InvalidAccountData),
)?;
wallet.name = name;
wallet.total_shares = total_shares;
wallet.total_inflow = 0;
wallet.last_inflow = 0;
wallet.disburse_cycles = 0;
wallet.total_members = 0;
wallet.tentacles = wallet.to_account_info().key();
wallet.authority = *ctx.accounts.authority.key;
wallet.remaining_flow = ctx.accounts.wallet_token_account.amount;
wallet.mint = ctx.accounts.mint.to_account_info().key();
wallet.bump_seed = bump;
wallet.token_account = ctx.accounts.wallet_token_account.to_account_info().key();
wallet.total_available_shares = total_shares;
wallet.members = Vec::with_capacity(5);
Ok(())
}
pub fn add_member(ctx: Context<AddMember>, shares: u64) -> Result<()> {
let wallet = &mut ctx.accounts.wallet;
if wallet.total_members >= 5 {
return Err(TentaClesError::MaxMembersReached.into());
}
if wallet.total_available_shares < shares {
return Err(TentaClesError::NotEnoughAvailableShares.into());
}
let member_pubkey = *ctx.accounts.member.key;
if wallet.members.iter().any(|m| m.member == member_pubkey) {
return Err(TentaClesError::DoubleMember.into());
}
let mint = wallet.mint;
let member_ata = ctx.accounts.member_token_account.key();
assert_ata_for_pubkey(&member_ata, &*ctx.accounts.member.key, &mint, Some(TentaClesError::MintMismatch))?;
let member_info = MemberInfo {
member: *ctx.accounts.member.key,
shares,
disburse_cycles: 0,
member_token_account: ctx.accounts.member_token_account.key(),
};
wallet.total_members += 1;
wallet.total_available_shares -= shares;
wallet.members.push(member_info);
Ok(())
}
pub fn distribute(ctx: Context<Distribute>, member_pubkey: Pubkey) -> Result<()> {
let wallet = &mut ctx.accounts.wallet;
let wallet_token_account = &ctx.accounts.wallet_token_account;
let member_token_account = &ctx.accounts.member_token_account;
let token_program = &ctx.accounts.token_program;
let pda_as_authority = &ctx.accounts.pda_as_authority;
let current_balance = wallet_token_account.amount;
msg!("Current balance before: {}", current_balance);
assert_ata_for_keys(&wallet_token_account.key(), &wallet.key(), &wallet.mint, Some(TentaClesError::MintMismatch))?;
let total_shares = wallet.total_shares;
if total_shares == 0 {
return Err(TentaClesError::NotEnoughAvailableShares.into());
}
let member_index = wallet.members.iter().position(|m| m.member == member_pubkey)
.ok_or_else(|| TentaClesError::InvalidAccountData)?;
if wallet.members[member_index].disburse_cycles == wallet.disburse_cycles && wallet.remaining_flow != 0 {
return Err(TentaClesError::UnclaimedAmountExists.into());
}
let amount_to_distribute;
if wallet.remaining_flow == 0 {
amount_to_distribute = (current_balance * wallet.members[member_index].shares) / total_shares;
wallet.last_inflow = current_balance;
wallet.remaining_flow = current_balance - amount_to_distribute;
wallet.total_inflow += current_balance;
wallet.disburse_cycles += 1;
} else {
amount_to_distribute = (wallet.last_inflow * wallet.members[member_index].shares) / total_shares;
wallet.remaining_flow -= amount_to_distribute;
}
msg!("Amount to distribute: {}", amount_to_distribute);
{
let wallet_name_as_bytes = wallet.name.as_bytes();
let wallet_seeds = &[b"split_wallet".as_ref(), wallet_name_as_bytes, &[wallet.bump_seed]];
let wallet_signer = &[&wallet_seeds[..]];
let cpi_accounts = Transfer {
from: wallet_token_account.to_account_info(),
to: member_token_account.to_account_info(),
authority: pda_as_authority.to_account_info(),
};
let cpi_context = CpiContext::new_with_signer(token_program.to_account_info(), cpi_accounts, wallet_signer);
token::transfer(cpi_context, amount_to_distribute)?;
}
let balance_after = wallet_token_account.amount;
msg!("Balance After: {}", balance_after);
{
let member_info = &mut wallet.members[member_index];
member_info.disburse_cycles += 1;
}
let all_members_claimed = wallet.members.iter().all(|m| m.disburse_cycles == wallet.disburse_cycles);
if all_members_claimed {
wallet.remaining_flow = 0;
} else {
wallet.remaining_flow = balance_after;
}
Ok(())
}