@@ -32,6 +32,7 @@ use indexmap::IndexMap;
3232use invoice:: { AddressPayload , UnknownNetwork } ;
3333use percent_encoding:: { utf8_percent_encode, AsciiSet , CONTROLS } ;
3434use rgb:: { ChainNet , ContractId , SchemaId , SecretSeal } ;
35+ use strict_types:: FieldName ;
3536
3637use crate :: invoice:: { Beneficiary , InvoiceState , Pay2Vout , RgbInvoice , RgbTransport , XChainNet } ;
3738
@@ -382,14 +383,25 @@ impl FromStr for RgbInvoice {
382383 Err ( _) => return Err ( InvoiceParseError :: InvalidSchemaId ( schema_str. to_string ( ) ) ) ,
383384 } ;
384385
385- let Some ( assignment) = path. next ( ) else {
386- return Err ( InvoiceParseError :: AssignmentMissed ) ;
386+ let ( assignment_name, assignment) = match ( path. next ( ) , path. next ( ) ) {
387+ ( None , _) => return Err ( InvoiceParseError :: AssignmentMissed ) ,
388+ ( Some ( first) , Some ( second) ) => {
389+ let name = FieldName :: try_from ( first. as_str ( ) . to_owned ( ) )
390+ . map_err ( |e| InvoiceParseError :: InvalidQueryParam ( e. to_string ( ) ) ) ?;
391+ ( Some ( name) , second. as_str ( ) . to_owned ( ) )
392+ }
393+ ( Some ( first) , None ) => ( None , first. as_str ( ) . to_owned ( ) ) ,
387394 } ;
395+
396+ // Check no extra path segments remain
397+ if path. next ( ) . is_some ( ) {
398+ return Err ( InvoiceParseError :: Invalid ) ;
399+ }
400+
388401 let ( amount, beneficiary) = assignment
389- . as_str ( )
390402 . split_once ( '+' )
391403 . map ( |( a, b) | ( Some ( a) , b) )
392- . unwrap_or ( ( None , assignment. as_str ( ) ) ) ;
404+ . unwrap_or ( ( None , & assignment) ) ;
393405 let ( value, beneficiary_str) = match ( amount, beneficiary) {
394406 ( Some ( a) , b) => (
395407 InvoiceState :: from_str ( a) . map_err ( |_| InvoiceParseError :: Data ( a. to_string ( ) ) ) ?,
@@ -427,7 +439,7 @@ impl FromStr for RgbInvoice {
427439 transports,
428440 contract,
429441 schema,
430- assignment_name : None ,
442+ assignment_name,
431443 beneficiary,
432444 assignment_state : value,
433445 expiry,
@@ -835,4 +847,41 @@ mod test {
835847 ) ) ;
836848 assert_eq ! ( Pay2Vout :: from_str( & p. to_string( ) ) . unwrap( ) , p) ;
837849 }
850+
851+ #[ test]
852+ fn parse_with_assignment_name ( ) {
853+ let original_invoice = RgbInvoice {
854+ transports : vec ! [ RgbTransport :: UnspecifiedMeans ] ,
855+ contract : ContractId :: from_str ( "11Fa!$Dk-rUWXhy8-7H35qXm-pLGGLOo-txBWUgj-tbOaSbI" ) . ok ( ) ,
856+ schema : SchemaId :: from_str ( "Il7xqc$4yuca6X0oy0kN27e13ieIAxAv43uVfpG$gYE" ) . ok ( ) ,
857+ assignment_name : Some ( "owner" . into ( ) ) ,
858+ beneficiary : XChainNet :: with (
859+ ChainNet :: BitcoinMainnet ,
860+ Beneficiary :: BlindedSeal (
861+ SecretSeal :: from_str ( "zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F" )
862+ . unwrap ( ) ,
863+ ) ,
864+ ) ,
865+ assignment_state : InvoiceState :: Amount ( Amount :: from ( 100u64 ) ) , // Example amount
866+ expiry : None ,
867+ unknown_query : IndexMap :: new ( ) ,
868+ } ;
869+
870+ let invoice_str = original_invoice. to_string ( ) ;
871+
872+ assert ! ( invoice_str. contains( "/owner/" ) ) ;
873+ let expected_str_fragment = "Il7xqc$4yuca6X0oy0kN27e13ieIAxAv43uVfpG$gYE/owner/BF+bc:\
874+ utxob:zlVS28Rb-amM5lih-ONXGACC-IUWD0Y$-0JXcnWZ-MQn8VEI-B39!F";
875+ assert ! ( invoice_str. contains( expected_str_fragment) ) ;
876+
877+ let deserialized_invoice =
878+ RgbInvoice :: from_str ( & invoice_str) . expect ( "Failed to deserialize invoice string" ) ;
879+
880+ assert_eq ! ( deserialized_invoice. assignment_name, original_invoice. assignment_name, ) ;
881+
882+ assert_eq ! ( deserialized_invoice. contract, original_invoice. contract, ) ;
883+ assert_eq ! ( deserialized_invoice. schema, original_invoice. schema, ) ;
884+ assert_eq ! ( deserialized_invoice. beneficiary, original_invoice. beneficiary, ) ;
885+ assert_eq ! ( deserialized_invoice. assignment_state, original_invoice. assignment_state, ) ;
886+ }
838887}
0 commit comments