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