hapi_core/checker/
mod.rs

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}