1mod address_data;
2
3use anchor_lang::prelude::*;
4use std::str::FromStr;
5
6use super::state::address::Category;
7use crate::error::ErrorCode;
8use address_data::AddressData;
9
10const MAINNET_SOLANA_NETWORK: &str = "GTBRKbzBtqDTvbBDmzAHRmUifyHqFGACgUxFrGHQgq4S";
11const DEVNET_SOLANA_NETWORK: &str = "GqZKWhUe7ymmZRxTY3LzW19HzT5cDEQE7m3zdtNW6MsD";
12
13pub enum HapiEnvironment {
14 Devnet,
15 Mainnet,
16}
17
18pub struct HapiChecker {
19 program_id: Pubkey,
20 solana_network: Pubkey,
21 max_risk: u8,
22 ignored_categories: Vec<Category>,
23}
24
25impl HapiChecker {
26 pub fn new(environment: HapiEnvironment) -> Self {
27 let (program_id, solana_network) = match environment {
28 HapiEnvironment::Devnet => (
29 crate::id(),
30 Pubkey::from_str(DEVNET_SOLANA_NETWORK).unwrap(),
31 ),
32 HapiEnvironment::Mainnet => (
33 crate::id(),
34 Pubkey::from_str(MAINNET_SOLANA_NETWORK).unwrap(),
35 ),
36 };
37
38 Self {
39 program_id,
40 solana_network,
41 max_risk: 0,
42 ignored_categories: Vec::new(),
43 }
44 }
45
46 pub fn max_risk(&mut self, risk: u8) -> &mut Self {
47 self.max_risk = if risk > 10 { 10 } else { risk };
48 self
49 }
50
51 pub fn ignore_category(&mut self, category: Category) -> &mut Self {
52 if self.ignored_categories.iter().position(|c| c == &category) == None {
53 self.ignored_categories.push(category);
54 }
55 self
56 }
57
58 pub fn get_hapi_address_seeds<'a>(&'a self, account: &'a Pubkey) -> [&'a [u8]; 4] {
59 [
60 b"address",
61 self.solana_network.as_ref(),
62 account.as_ref(),
63 &[0u8; 32],
64 ]
65 }
66
67 pub fn get_hapi_address(&self, account: &Pubkey) -> (Pubkey, u8) {
68 Pubkey::find_program_address(&self.get_hapi_address_seeds(account), &self.program_id)
69 }
70
71 pub fn check_address_risk(
72 &self,
73 address_info: &AccountInfo,
74 payer_account: &Pubkey,
75 ) -> Result<()> {
76 let (address_account, address_bump) = self.get_hapi_address(payer_account);
77
78 if address_account != address_info.key() {
79 return Err(ErrorCode::UnexpectedAccount.into());
80 }
81
82 if address_info.data_is_empty() {
83 return Ok(());
84 }
85
86 if address_info.owner.ne(&self.program_id) {
87 return Err(ErrorCode::IllegalOwner.into());
88 }
89
90 let data = AddressData::from(address_info);
91
92 if let Ok(data) = data {
93 if address_bump != data.bump {
94 return Err(ErrorCode::UnexpectedAccount.into());
95 }
96
97 if self.ignored_categories.iter().any(|i| i == &data.category) {
98 return Ok(());
99 }
100
101 if data.risk > self.max_risk {
102 return Err(ErrorCode::HighAccountRisk.into());
103 }
104 }
105
106 Ok(())
107 }
108}