use solana_program::pubkey::Pubkey;
use crate::state::{KycPolicy, KycRecord};
pub fn verify_kyc_for_transfer(
policy: KycPolicy,
destination_owner: &Pubkey,
global: Option<&KycRecord>,
offering: Option<&KycRecord>,
) -> Result<(), crate::error::HookError> {
let check_global = matches!(policy, KycPolicy::GlobalOnly | KycPolicy::Both);
let check_offering = matches!(policy, KycPolicy::OfferingOnly | KycPolicy::Both);
if check_global {
let record = global.ok_or(crate::error::HookError::KycRecordNotFound)?;
if record.user != *destination_owner || !record.is_verified() {
return Err(crate::error::HookError::RecipientNotKycVerified);
}
}
if check_offering {
let record = offering.ok_or(crate::error::HookError::KycRecordNotFound)?;
if record.user != *destination_owner || !record.is_verified() {
return Err(crate::error::HookError::RecipientNotKycVerified);
}
}
Ok(())
}
pub fn token_account_owner(data: &[u8]) -> Result<Pubkey, crate::error::HookError> {
if data.len() < crate::consts::TOKEN_ACCOUNT_OWNER_OFFSET + 32 {
return Err(crate::error::HookError::InvalidTokenAccountOwner);
}
Pubkey::try_from(
&data[crate::consts::TOKEN_ACCOUNT_OWNER_OFFSET
..crate::consts::TOKEN_ACCOUNT_OWNER_OFFSET + 32],
)
.map_err(|_| crate::error::HookError::InvalidTokenAccountOwner)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::{KycPolicy, RecordKind};
use crate::test_utils::kyc_record as record;
#[test]
fn both_requires_two_verified_records() {
let user = Pubkey::new_unique();
let issuer_id = [9u8; 16];
let g = record(user, issuer_id, true, RecordKind::Global);
let o = record(user, issuer_id, true, RecordKind::Offering);
verify_kyc_for_transfer(KycPolicy::Both, &user, Some(&g), Some(&o)).unwrap();
}
#[test]
fn global_only_rejects_unverified() {
let user = Pubkey::new_unique();
let issuer_id = [9u8; 16];
let g = record(user, issuer_id, false, RecordKind::Global);
assert_eq!(
verify_kyc_for_transfer(KycPolicy::GlobalOnly, &user, Some(&g), None),
Err(crate::error::HookError::RecipientNotKycVerified)
);
}
#[test]
fn token_account_owner_rejects_short_data() {
assert_eq!(
token_account_owner(&[0u8; 32]),
Err(crate::error::HookError::InvalidTokenAccountOwner)
);
}
#[test]
fn token_account_owner_reads_correct_offset() {
let owner = Pubkey::new_unique();
let mut data = vec![0u8; 64];
data[32..64].copy_from_slice(owner.as_ref());
assert_eq!(token_account_owner(&data).unwrap(), owner);
}
}