@@ -25,8 +25,8 @@ pub struct AMLThresholds {
2525impl Default for AMLThresholds {
2626 fn default ( ) -> Self {
2727 Self {
28- ctr_threshold : 10000.0 , // FinCEN CTR requirement
29- sar_threshold : 5000.0 , // FinCEN SAR guideline
28+ ctr_threshold : 10000.0 , // FinCEN CTR requirement
29+ sar_threshold : 5000.0 , // FinCEN SAR guideline
3030 structuring_threshold : 9500.0 , // Just under $10k
3131 }
3232 }
@@ -120,9 +120,17 @@ impl AMLChecker {
120120 }
121121
122122 // Sanctioned entity check
123- if self . is_sanctioned_entity ( & transaction. from_account )
124- || self . is_sanctioned_entity ( & transaction. to_account )
125- {
123+ let from_sanctioned = transaction
124+ . from_account
125+ . as_deref ( )
126+ . map ( |a| self . is_sanctioned_entity ( a) )
127+ . unwrap_or ( false ) ;
128+ let to_sanctioned = transaction
129+ . to_account
130+ . as_deref ( )
131+ . map ( |a| self . is_sanctioned_entity ( a) )
132+ . unwrap_or ( false ) ;
133+ if from_sanctioned || to_sanctioned {
126134 red_flags. push ( AMLRedFlag {
127135 flag_type : RedFlagType :: SanctionedEntity ,
128136 description : "Transaction involves sanctioned entity" . to_string ( ) ,
@@ -134,7 +142,11 @@ impl AMLChecker {
134142
135143 // Cross-border transaction check
136144 if let Some ( ref metadata) = transaction. metadata {
137- if metadata. get ( "cross_border" ) . and_then ( |v| v. as_bool ( ) ) == Some ( true ) {
145+ if metadata
146+ . get ( "cross_border" )
147+ . map ( |v| v == "true" )
148+ . unwrap_or ( false )
149+ {
138150 red_flags. push ( AMLRedFlag {
139151 flag_type : RedFlagType :: CrossBorder ,
140152 description : "Cross-border transaction requires additional due diligence"
@@ -146,20 +158,20 @@ impl AMLChecker {
146158 }
147159
148160 // Cash intensive check
149- if transaction. transaction_type == "cash_deposit"
150- || transaction. transaction_type == "cash_withdrawal"
161+ if matches ! (
162+ transaction. transaction_type,
163+ crate :: TransactionType :: Deposit | crate :: TransactionType :: Withdrawal
164+ ) && transaction. amount >= 5000.0
151165 {
152- if transaction. amount >= 5000.0 {
153- red_flags. push ( AMLRedFlag {
154- flag_type : RedFlagType :: CashIntensive ,
155- description : format ! (
156- "Large cash {} of {}" ,
157- transaction. transaction_type, transaction. amount
158- ) ,
159- severity : AlertSeverity :: High ,
160- } ) ;
161- risk_score += 25 ;
162- }
166+ red_flags. push ( AMLRedFlag {
167+ flag_type : RedFlagType :: CashIntensive ,
168+ description : format ! (
169+ "Large cash {} of {}" ,
170+ transaction. transaction_type, transaction. amount
171+ ) ,
172+ severity : AlertSeverity :: High ,
173+ } ) ;
174+ risk_score += 25 ;
163175 }
164176
165177 AMLResult {
@@ -209,7 +221,13 @@ impl KYCValidator {
209221 let mut warnings = Vec :: new ( ) ;
210222
211223 // Required fields for KYC
212- let required = [ "full_name" , "date_of_birth" , "address" , "id_number" , "id_type" ] ;
224+ let required = [
225+ "full_name" ,
226+ "date_of_birth" ,
227+ "address" ,
228+ "id_number" ,
229+ "id_type" ,
230+ ] ;
213231
214232 for field in & required {
215233 if customer_data. get ( field) . is_none ( ) {
@@ -220,21 +238,30 @@ impl KYCValidator {
220238 // Check for enhanced due diligence triggers
221239 if let Some ( country) = customer_data. get ( "country" ) . and_then ( |v| v. as_str ( ) ) {
222240 if Self :: is_high_risk_jurisdiction ( country) {
223- warnings. push ( "Customer from high-risk jurisdiction - Enhanced Due Diligence required" . to_string ( ) ) ;
241+ warnings. push (
242+ "Customer from high-risk jurisdiction - Enhanced Due Diligence required"
243+ . to_string ( ) ,
244+ ) ;
224245 }
225246 }
226247
227- if let Some ( pep) = customer_data. get ( "politically_exposed_person" ) . and_then ( |v| v. as_bool ( ) ) {
248+ if let Some ( pep) = customer_data
249+ . get ( "politically_exposed_person" )
250+ . and_then ( |v| v. as_bool ( ) )
251+ {
228252 if pep {
229- warnings. push ( "Politically Exposed Person - Enhanced Due Diligence required" . to_string ( ) ) ;
253+ warnings. push (
254+ "Politically Exposed Person - Enhanced Due Diligence required" . to_string ( ) ,
255+ ) ;
230256 }
231257 }
232258
259+ let requires_enhanced_dd = !warnings. is_empty ( ) ;
233260 KYCValidationResult {
234261 valid : missing_fields. is_empty ( ) ,
235262 missing_fields,
236263 warnings,
237- requires_enhanced_dd : !warnings . is_empty ( ) ,
264+ requires_enhanced_dd,
238265 }
239266 }
240267
@@ -258,23 +285,24 @@ mod tests {
258285 use super :: * ;
259286 use chrono:: Utc ;
260287
261- fn create_test_transaction ( amount : f64 , txn_type : & str ) -> Transaction {
288+ fn create_test_transaction ( amount : f64 , txn_type : crate :: TransactionType ) -> Transaction {
262289 Transaction {
263- id : "TXN-001" . to_string ( ) ,
264- from_account : "ACC-123" . to_string ( ) ,
265- to_account : "ACC-456" . to_string ( ) ,
290+ transaction_id : "TXN-001" . to_string ( ) ,
291+ from_account : Some ( "ACC-123" . to_string ( ) ) ,
292+ to_account : Some ( "ACC-456" . to_string ( ) ) ,
266293 amount,
267294 currency : "USD" . to_string ( ) ,
268295 timestamp : Utc :: now ( ) ,
269- transaction_type : txn_type. to_string ( ) ,
296+ transaction_type : txn_type,
297+ user_id : "USER-001" . to_string ( ) ,
270298 metadata : None ,
271299 }
272300 }
273301
274302 #[ test]
275303 fn test_ctr_requirement ( ) {
276304 let checker = AMLChecker :: new ( ) ;
277- let txn = create_test_transaction ( 15000.0 , "transfer" ) ;
305+ let txn = create_test_transaction ( 15000.0 , crate :: TransactionType :: Transfer ) ;
278306 let result = checker. check_compliance ( & txn) ;
279307
280308 assert ! ( result. requires_ctr) ;
@@ -287,7 +315,7 @@ mod tests {
287315 #[ test]
288316 fn test_structuring_detection ( ) {
289317 let checker = AMLChecker :: new ( ) ;
290- let txn = create_test_transaction ( 9800.0 , "transfer" ) ;
318+ let txn = create_test_transaction ( 9800.0 , crate :: TransactionType :: Transfer ) ;
291319 let result = checker. check_compliance ( & txn) ;
292320
293321 assert ! ( result. requires_sar) ;
@@ -300,8 +328,8 @@ mod tests {
300328 #[ test]
301329 fn test_sanctioned_entity ( ) {
302330 let checker = AMLChecker :: new ( ) ;
303- let mut txn = create_test_transaction ( 1000.0 , "transfer" ) ;
304- txn. from_account = "OFAC-SANCTIONED-001" . to_string ( ) ;
331+ let mut txn = create_test_transaction ( 1000.0 , crate :: TransactionType :: Transfer ) ;
332+ txn. from_account = Some ( "OFAC-SANCTIONED-001" . to_string ( ) ) ;
305333
306334 let result = checker. check_compliance ( & txn) ;
307335
@@ -316,7 +344,7 @@ mod tests {
316344 #[ test]
317345 fn test_cash_intensive ( ) {
318346 let checker = AMLChecker :: new ( ) ;
319- let txn = create_test_transaction ( 8000.0 , "cash_deposit" ) ;
347+ let txn = create_test_transaction ( 8000.0 , crate :: TransactionType :: Deposit ) ;
320348 let result = checker. check_compliance ( & txn) ;
321349
322350 assert ! ( result
@@ -328,8 +356,10 @@ mod tests {
328356 #[ test]
329357 fn test_cross_border ( ) {
330358 let checker = AMLChecker :: new ( ) ;
331- let mut txn = create_test_transaction ( 5000.0 , "transfer" ) ;
332- txn. metadata = Some ( serde_json:: json!( { "cross_border" : true } ) ) ;
359+ let mut txn = create_test_transaction ( 5000.0 , crate :: TransactionType :: Transfer ) ;
360+ let mut metadata = std:: collections:: HashMap :: new ( ) ;
361+ metadata. insert ( "cross_border" . to_string ( ) , "true" . to_string ( ) ) ;
362+ txn. metadata = Some ( metadata) ;
333363
334364 let result = checker. check_compliance ( & txn) ;
335365
0 commit comments