hydra/utils/validation/
mod.rs1use crate::error::HydraError;
2use crate::state::{Fanout, MembershipModel};
3use anchor_lang::prelude::*;
4use anchor_lang::solana_program::instruction::Instruction;
5use anchor_spl::token::TokenAccount;
6use mpl_token_metadata::state::Metadata;
7
8pub fn assert_derivation(
9 program_id: &Pubkey,
10 account: &AccountInfo,
11 path: &[&[u8]],
12 error: Option<error::Error>,
13) -> Result<u8> {
14 let (key, bump) = Pubkey::find_program_address(&path, program_id);
15 if key != *account.key {
16 if error.is_some() {
17 let err = error.unwrap();
18 msg!("Derivation {:?}", err);
19 return Err(err.into());
20 }
21 msg!("DerivedKeyInvalid");
22 return Err(HydraError::DerivedKeyInvalid.into());
23 }
24 Ok(bump)
25}
26
27pub fn assert_owned_by(account: &AccountInfo, owner: &Pubkey) -> Result<()> {
28 if account.owner != owner {
29 Err(HydraError::IncorrectOwner.into())
30 } else {
31 Ok(())
32 }
33}
34
35pub fn assert_membership_model(
36 fanout: &Account<Fanout>,
37 model: MembershipModel,
38) -> Result<()> {
39 if fanout.membership_model != model {
40 return Err(HydraError::InvalidMembershipModel.into());
41 }
42 Ok(())
43}
44
45pub fn assert_ata(
46 account: &AccountInfo,
47 target: &Pubkey,
48 mint: &Pubkey,
49 err: Option<error::Error>,
50) -> Result<u8> {
51 assert_derivation(
52 &anchor_spl::associated_token::ID,
53 &account.to_account_info(),
54 &[
55 target.as_ref(),
56 anchor_spl::token::ID.as_ref(),
57 mint.as_ref(),
58 ],
59 err,
60 )
61}
62
63pub fn assert_shares_distributed(fanout: &Account<Fanout>) -> Result<()> {
64 if fanout.total_available_shares != 0 {
65 return Err(HydraError::SharesArentAtMax.into());
66 }
67 Ok(())
68}
69
70pub fn assert_holding(
71 owner: &AccountInfo,
72 token_account: &Account<TokenAccount>,
73 mint_info: &AccountInfo,
74) -> Result<()> {
75 assert_owned_by(mint_info, &spl_token::id())?;
76 let token_account_info = token_account.to_account_info();
77 assert_owned_by(&token_account_info, &spl_token::id())?;
78 if token_account.owner != *owner.key {
79 return Err(HydraError::IncorrectOwner.into());
80 }
81 if token_account.amount < 1 {
82 return Err(HydraError::WalletDoesNotOwnMembershipToken.into());
83 }
84 if token_account.mint != mint_info.key() {
85 return Err(HydraError::MintDoesNotMatch.into());
86 }
87 Ok(())
88}
89
90pub fn assert_distributed(
91 ix: Instruction,
92 subject: &Pubkey,
93 membership_model: MembershipModel,
94) -> Result<()> {
95 if ix.program_id != crate::id() {
96 return Err(HydraError::MustDistribute.into());
97 }
98 let instruction_id = match membership_model {
99 MembershipModel::Wallet => [252, 168, 167, 66, 40, 201, 182, 163],
100 MembershipModel::NFT => [108, 240, 68, 81, 144, 83, 58, 153],
101 MembershipModel::Token => [126, 105, 46, 135, 28, 36, 117, 212],
102 };
103 if instruction_id != ix.data[0..8] {
104 return Err(HydraError::MustDistribute.into());
105 }
106 if subject != &ix.accounts[1].pubkey {
107 return Err(HydraError::MustDistribute.into());
108 }
109 Ok(())
110}
111
112pub fn assert_valid_metadata(
113 metadata_account: &AccountInfo,
114 mint: &AccountInfo,
115) -> Result<Metadata> {
116 let meta = Metadata::from_account_info(metadata_account)?;
117 if meta.mint != *mint.key {
118 return Err(HydraError::InvalidMetadata.into());
119 }
120 Ok(meta)
121}