hydra/processors/transfer_shares/
transfer_shares.rs

1use crate::error::HydraError;
2use crate::state::{Fanout, FanoutMembershipVoucher};
3use crate::utils::validation::assert_distributed;
4use crate::MembershipModel;
5use anchor_lang::prelude::*;
6use anchor_lang::solana_program::sysvar;
7use anchor_lang::solana_program::sysvar::instructions::get_instruction_relative;
8
9#[derive(Accounts)]
10#[instruction(shares: u64)]
11pub struct TransferShares<'info> {
12    pub authority: Signer<'info>,
13    /// CHECK: Native Account
14    pub member: UncheckedAccount<'info>,
15    /// CHECK: Native Account
16    pub membership_key: UncheckedAccount<'info>,
17    #[account(
18    mut,
19    seeds = [b"fanout-config", fanout.name.as_bytes()],
20    has_one= authority,
21    bump = fanout.bump_seed,
22    )]
23    pub fanout: Account<'info, Fanout>,
24    #[account(
25    seeds = [b"fanout-membership", fanout.key().as_ref(), membership_key.key().as_ref()],
26    bump,
27    has_one = fanout,
28    )]
29    pub from_membership_account: Account<'info, FanoutMembershipVoucher>,
30    #[account(
31    seeds = [b"fanout-membership", fanout.key().as_ref(), membership_key.key().as_ref()],
32    bump,
33    has_one = fanout,
34    )]
35    pub to_membership_account: Account<'info, FanoutMembershipVoucher>,
36    #[account(address = sysvar::instructions::id())]
37    /// CHECK: Instructions SYSVAR
38    pub instructions: UncheckedAccount<'info>,
39}
40
41pub fn transfer_shares(ctx: Context<TransferShares>, shares: u64) -> Result<()> {
42    let fanout = &mut ctx.accounts.fanout;
43    let from_membership_account = &mut ctx.accounts.from_membership_account;
44    let to_membership_account = &mut ctx.accounts.to_membership_account;
45    let ixs = &ctx.accounts.instructions;
46    let member = &ctx.accounts.member;
47    let prev_ix = get_instruction_relative(-1, ixs).unwrap();
48    assert_distributed(prev_ix, member.key, fanout.membership_model)?;
49
50    if to_membership_account.key() == from_membership_account.key() {
51        return Err(HydraError::TransferNotSupported.into());
52    }
53
54    if from_membership_account.shares < shares {
55        return Err(HydraError::InsufficientShares.into());
56    }
57
58    if fanout.membership_model != MembershipModel::NFT
59        || fanout.membership_model != MembershipModel::Wallet
60    {
61        return Err(HydraError::TransferNotSupported.into());
62    }
63    from_membership_account.shares -= shares;
64    to_membership_account.shares += shares;
65    Ok(())
66}