@@ -35,6 +35,27 @@ pub fn convert_to_homograph_safe_chars(input: &str) -> String {
3535 . collect ( )
3636}
3737
38+ fn extract_dpns_label ( name : & str ) -> & str {
39+ if let Some ( dot_pos) = name. rfind ( '.' ) {
40+ let ( label_part, suffix) = name. split_at ( dot_pos) ;
41+ if suffix. eq_ignore_ascii_case ( ".dash" ) {
42+ return label_part;
43+ }
44+ }
45+ name
46+ }
47+
48+ /// Strip an optional case-insensitive `.dash` suffix and apply DPNS
49+ /// homograph-safe normalization, producing a value suitable for matching
50+ /// against the `normalizedLabel` field of `domain` documents.
51+ ///
52+ /// Accepts either a bare label (e.g. `"alice"`) or a full DPNS name
53+ /// (e.g. `"alice.dash"`, `"Alice.DASH"`) and returns the normalized label
54+ /// (e.g. `"a11ce"`).
55+ fn normalize_dpns_label ( input : & str ) -> String {
56+ convert_to_homograph_safe_chars ( extract_dpns_label ( input) )
57+ }
58+
3859/// Check if a username is valid according to DPNS rules
3960///
4061/// A username is valid if:
@@ -365,19 +386,22 @@ impl Sdk {
365386 ///
366387 /// # Arguments
367388 ///
368- /// * `label` - The username label to check (e.g., "alice")
389+ /// * `name` - The username label (e.g., "alice") or full DPNS name
390+ /// (e.g., "alice.dash"). The `.dash` suffix is matched
391+ /// case-insensitively and stripped before normalization, mirroring
392+ /// [`Sdk::resolve_dpns_name`].
369393 ///
370394 /// # Returns
371395 ///
372396 /// Returns `true` if the name is available, `false` if it's taken
373- pub async fn is_dpns_name_available ( & self , label : & str ) -> Result < bool , Error > {
397+ pub async fn is_dpns_name_available ( & self , name : & str ) -> Result < bool , Error > {
374398 use crate :: platform:: documents:: document_query:: DocumentQuery ;
375399 use drive:: query:: WhereClause ;
376400 use drive:: query:: WhereOperator ;
377401
378402 let dpns_contract = self . fetch_dpns_contract ( ) . await ?;
379403
380- let normalized_label = convert_to_homograph_safe_chars ( label ) ;
404+ let normalized_label = normalize_dpns_label ( name ) ;
381405
382406 // Query for existing domain with this label
383407 let query = DocumentQuery {
@@ -422,29 +446,13 @@ impl Sdk {
422446
423447 let dpns_contract = self . fetch_dpns_contract ( ) . await ?;
424448
425- // Extract label from full name if needed
426- // Handle both "alice" and "alice.dash" formats
427- let label = if let Some ( dot_pos) = name. rfind ( '.' ) {
428- let ( label_part, suffix) = name. split_at ( dot_pos) ;
429- // Strip ".dash" / ".DASH" / mixed case — DPNS itself is case-insensitive.
430- if suffix. eq_ignore_ascii_case ( ".dash" ) {
431- label_part
432- } else {
433- // If it's not ".dash", treat the whole thing as the label
434- name
435- }
436- } else {
437- // No dot found, use the whole name as the label
438- name
439- } ;
449+ let normalized_label = normalize_dpns_label ( name) ;
440450
441- // Validate the label before proceeding
442- if label . is_empty ( ) {
451+ // Validate the normalized label before proceeding
452+ if normalized_label . is_empty ( ) {
443453 return Ok ( None ) ;
444454 }
445455
446- let normalized_label = convert_to_homograph_safe_chars ( label) ;
447-
448456 // Query for domain with this label
449457 let query = DocumentQuery {
450458 data_contract : dpns_contract,
@@ -499,6 +507,40 @@ mod tests {
499507 assert_eq ! ( convert_to_homograph_safe_chars( "test123" ) , "test123" ) ;
500508 }
501509
510+ #[ test]
511+ fn test_normalize_dpns_label_strips_dash_suffix_case_insensitively ( ) {
512+ // Bare label and full name normalize to the same value, regardless
513+ // of the case of the .dash suffix. This is the contract that
514+ // `is_dpns_name_available` and `resolve_dpns_name` share so that
515+ // queries against `normalizedLabel` agree.
516+ let expected = "a11ce" ;
517+ assert_eq ! ( normalize_dpns_label( "alice" ) , expected) ;
518+ assert_eq ! ( normalize_dpns_label( "alice.dash" ) , expected) ;
519+ assert_eq ! ( normalize_dpns_label( "alice.DASH" ) , expected) ;
520+ assert_eq ! ( normalize_dpns_label( "Alice.DaSh" ) , expected) ;
521+ assert_eq ! ( normalize_dpns_label( "ALICE.DASH" ) , expected) ;
522+
523+ // Non-.dash suffixes are not stripped (they are treated as part of
524+ // the label and normalized whole).
525+ assert_eq ! ( normalize_dpns_label( "alice.eth" ) , "a11ce.eth" ) ;
526+
527+ // Empty / suffix-only inputs normalize to an empty label.
528+ assert_eq ! ( normalize_dpns_label( "" ) , "" ) ;
529+ assert_eq ! ( normalize_dpns_label( ".dash" ) , "" ) ;
530+ assert_eq ! ( normalize_dpns_label( ".DASH" ) , "" ) ;
531+ }
532+
533+ #[ test]
534+ fn test_extract_dpns_label ( ) {
535+ assert_eq ! ( extract_dpns_label( "alice.dash" ) , "alice" ) ;
536+ assert_eq ! ( extract_dpns_label( "alice.DASH" ) , "alice" ) ;
537+ assert_eq ! ( extract_dpns_label( "alice.DaSh" ) , "alice" ) ;
538+ assert_eq ! ( extract_dpns_label( "Alice.DASH" ) , "Alice" ) ;
539+ assert_eq ! ( extract_dpns_label( "alice" ) , "alice" ) ;
540+ assert_eq ! ( extract_dpns_label( "alice.eth" ) , "alice.eth" ) ;
541+ assert_eq ! ( extract_dpns_label( ".dash" ) , "" ) ;
542+ }
543+
502544 #[ test]
503545 fn test_is_valid_username ( ) {
504546 // Valid usernames
0 commit comments