@@ -410,25 +410,13 @@ impl SocialPaymentContract {
410410
411411 // ── Payment methods ───────────────────────────────────────────────────────
412412
413- /// SC-005: Execute a P2P social payment using the configured Naira token.
414- /// For Public payments a 0.1% platform fee is routed to the treasury.
415- /// For Friends/Private payments the full amount goes to the receiver.
413+ /// SC-005 / Issue #965: Execute a P2P social payment using the configured
414+ /// Naira token. The recipient is identified by their registered username,
415+ /// which is resolved to an on-chain address via the configured UserRegistry
416+ /// contract before routing the payment. For Public payments a 0.1% platform
417+ /// fee is routed to the treasury. For Friends/Private payments the full
418+ /// amount goes to the receiver.
416419 pub fn pay (
417- env : Env ,
418- sender : Address ,
419- receiver : Address ,
420- token : Address ,
421- amount : i128 ,
422- memo : String ,
423- visibility : Visibility ,
424- ) {
425- sender. require_auth ( ) ;
426- execute_payment ( env, sender, receiver, token, amount, memo, visibility) ;
427- }
428-
429- /// Issue #519: Accept a recipient username, resolve the on-chain address via the
430- /// configured UserRegistry contract, then execute the standard payment flow.
431- pub fn pay_by_username (
432420 env : Env ,
433421 sender : Address ,
434422 recipient_username : String ,
@@ -446,11 +434,35 @@ impl SocialPaymentContract {
446434 . expect ( "user registry not configured" ) ;
447435
448436 let registry = UserRegistryClient :: new ( & env, & registry_id) ;
437+ // Username resolution — panics with "username not found" if unregistered.
449438 let receiver = registry. get_address ( & recipient_username) ;
450439
451440 execute_payment ( env, sender, receiver, token, amount, memo, visibility) ;
452441 }
453442
443+ /// Issue #519: Backward-compatible alias for `pay` that accepts a recipient
444+ /// username, resolves the on-chain address via the configured UserRegistry
445+ /// contract, then executes the standard payment flow.
446+ pub fn pay_by_username (
447+ env : Env ,
448+ sender : Address ,
449+ recipient_username : String ,
450+ token : Address ,
451+ amount : i128 ,
452+ memo : String ,
453+ visibility : Visibility ,
454+ ) {
455+ Self :: pay (
456+ env,
457+ sender,
458+ recipient_username,
459+ token,
460+ amount,
461+ memo,
462+ visibility,
463+ ) ;
464+ }
465+
454466 /// SC-037: Execute multiple payments in a single contract call.
455467 pub fn batch_pay ( env : Env , sender : Address , payments : Vec < Payment > ) {
456468 sender. require_auth ( ) ;
@@ -852,14 +864,17 @@ mod tests {
852864 // ── Public payment: fee deducted, event emitted ──────────────────────────
853865 #[ test]
854866 fn test_social_payment_public_visibility_deducts_fee ( ) {
855- let ( env, client, admin, treasury, sender, receiver) = setup ( ) ;
867+ let ( env, client, admin, treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
856868 let token = mint_token ( & env, & admin, & sender, 10_000 ) ;
857869 client. set_naira_token ( & token) ;
858870 let token_client = soroban_sdk:: token:: Client :: new ( & env, & token) ;
859871
872+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
873+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
874+
860875 client. pay (
861876 & sender,
862- & receiver ,
877+ & String :: from_str ( & env , "bob" ) ,
863878 & token,
864879 & 1000 ,
865880 & String :: from_str ( & env, "Public payment" ) ,
@@ -899,14 +914,17 @@ mod tests {
899914 #[ test]
900915 fn test_public_payment_fee_rounding_preserves_funds_at_boundaries ( ) {
901916 for amount in [ 1i128 , 1_000 , i128:: MAX ] {
902- let ( env, client, admin, treasury, sender, receiver) = setup ( ) ;
917+ let ( env, client, admin, treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
903918 let token = mint_token ( & env, & admin, & sender, amount) ;
904919 client. set_naira_token ( & token) ;
905920 let token_client = soroban_sdk:: token:: Client :: new ( & env, & token) ;
906921
922+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
923+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
924+
907925 client. pay (
908926 & sender,
909- & receiver ,
927+ & String :: from_str ( & env , "bob" ) ,
910928 & token,
911929 & amount,
912930 & String :: from_str ( & env, "Rounding boundary" ) ,
@@ -923,14 +941,17 @@ mod tests {
923941 // ── Private payment: no fee, full amount ─────────────────────────────────
924942 #[ test]
925943 fn test_social_payment_private_visibility_no_fee ( ) {
926- let ( env, client, admin, _treasury, sender, receiver) = setup ( ) ;
944+ let ( env, client, admin, _treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
927945 let token = mint_token ( & env, & admin, & sender, 10_000 ) ;
928946 client. set_naira_token ( & token) ;
929947 let token_client = soroban_sdk:: token:: Client :: new ( & env, & token) ;
930948
949+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
950+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
951+
931952 client. pay (
932953 & sender,
933- & receiver ,
954+ & String :: from_str ( & env , "bob" ) ,
934955 & token,
935956 & 1000 ,
936957 & String :: from_str ( & env, "Private" ) ,
@@ -944,14 +965,17 @@ mod tests {
944965 // ── Friends-only payment: no fee ─────────────────────────────────────────
945966 #[ test]
946967 fn test_social_payment_friends_visibility_no_fee ( ) {
947- let ( env, client, admin, treasury, sender, receiver) = setup ( ) ;
968+ let ( env, client, admin, treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
948969 let token = mint_token ( & env, & admin, & sender, 5_000 ) ;
949970 client. set_naira_token ( & token) ;
950971 let token_client = soroban_sdk:: token:: Client :: new ( & env, & token) ;
951972
973+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
974+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
975+
952976 client. pay (
953977 & sender,
954- & receiver ,
978+ & String :: from_str ( & env , "bob" ) ,
955979 & token,
956980 & 500 ,
957981 & String :: from_str ( & env, "Friends" ) ,
@@ -967,12 +991,14 @@ mod tests {
967991 #[ test]
968992 #[ ignore]
969993 fn test_pay_rejects_zero_amount ( ) {
970- let ( env, client, admin, _treasury, sender, receiver) = setup ( ) ;
994+ let ( env, client, admin, _treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
971995 let token = mint_token ( & env, & admin, & sender, 1_000 ) ;
972996 client. set_naira_token ( & token) ;
997+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
998+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
973999 let res = client. try_pay (
9741000 & sender,
975- & receiver ,
1001+ & String :: from_str ( & env , "bob" ) ,
9761002 & token,
9771003 & 0 ,
9781004 & String :: from_str ( & env, "bad" ) ,
@@ -1184,15 +1210,18 @@ mod tests {
11841210 #[ test]
11851211 #[ ignore] // pre-existing: assert!(..) in Soroban v20 causes non-unwinding panic
11861212 fn test_public_payment_rejects_non_naira_token_for_fee ( ) {
1187- let ( env, client, admin, treasury, sender, receiver) = setup ( ) ;
1213+ let ( env, client, admin, treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
11881214 let naira_token = mint_token ( & env, & admin, & sender, 10_000 ) ;
11891215 let junk_token = mint_token ( & env, & admin, & sender, 10_000 ) ;
11901216 let junk_client = soroban_sdk:: token:: Client :: new ( & env, & junk_token) ;
11911217 client. set_naira_token ( & naira_token) ;
11921218
1219+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
1220+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
1221+
11931222 let res = client. try_pay (
11941223 & sender,
1195- & receiver ,
1224+ & String :: from_str ( & env , "bob" ) ,
11961225 & junk_token,
11971226 & 1000 ,
11981227 & String :: from_str ( & env, "Junk public payment" ) ,
@@ -1208,12 +1237,15 @@ mod tests {
12081237 #[ test]
12091238 #[ ignore] // pre-existing: .expect() in Soroban v20 causes non-unwinding panic
12101239 fn test_pay_rejects_when_naira_token_not_configured ( ) {
1211- let ( env, client, admin, _treasury, sender, receiver) = setup ( ) ;
1240+ let ( env, client, admin, _treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
12121241 let token = mint_token ( & env, & admin, & sender, 1_000 ) ;
12131242
1243+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
1244+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
1245+
12141246 let res = client. try_pay (
12151247 & sender,
1216- & receiver ,
1248+ & String :: from_str ( & env , "bob" ) ,
12171249 & token,
12181250 & 100 ,
12191251 & String :: from_str ( & env, "Missing config" ) ,
@@ -1226,11 +1258,14 @@ mod tests {
12261258 // ── Adjust fee coefficient: updates value, affects payout calculation ─────
12271259 #[ test]
12281260 fn test_adjust_fee_coefficient ( ) {
1229- let ( env, client, admin, treasury, sender, receiver) = setup ( ) ;
1261+ let ( env, client, admin, treasury, sender, receiver, registry_id ) = setup_with_registry ( ) ;
12301262 let token = mint_token ( & env, & admin, & sender, 10_000 ) ;
12311263 client. set_naira_token ( & token) ;
12321264 let token_client = soroban_sdk:: token:: Client :: new ( & env, & token) ;
12331265
1266+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
1267+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
1268+
12341269 // Verify default is 10 (0.1%)
12351270 assert_eq ! ( client. fee_coefficient( ) , 10 ) ;
12361271
@@ -1240,7 +1275,7 @@ mod tests {
12401275
12411276 client. pay (
12421277 & sender,
1243- & receiver ,
1278+ & String :: from_str ( & env , "bob" ) ,
12441279 & token,
12451280 & 1000 ,
12461281 & String :: from_str ( & env, "Public payment" ) ,
@@ -1627,7 +1662,7 @@ mod tests {
16271662
16281663 client. pay (
16291664 & sender,
1630- & receiver ,
1665+ & String :: from_str ( & env , "bob" ) ,
16311666 & token,
16321667 & 1000 ,
16331668 & String :: from_str ( & env, "hi" ) ,
@@ -1651,14 +1686,18 @@ mod tests {
16511686 // ── SC-042: no registry / unregistered participants never break a payment ─
16521687 #[ test]
16531688 fn test_pay_event_username_defaults_to_empty_when_unregistered ( ) {
1654- // No registry configured at all — resolve_username must short-circuit.
1655- let ( env, client, admin, _treasury, sender, receiver) = setup ( ) ;
1689+ // Registry configured but neither participant registered a username —
1690+ // resolve_username must fall back to empty strings.
1691+ let ( env, client, admin, _treasury, sender, receiver, registry_id) = setup_with_registry ( ) ;
16561692 let token = mint_token ( & env, & admin, & sender, 10_000 ) ;
16571693 client. set_naira_token ( & token) ;
16581694
1695+ let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
1696+ registry_client. register ( & receiver, & String :: from_str ( & env, "bob" ) ) ;
1697+
16591698 client. pay (
16601699 & sender,
1661- & receiver ,
1700+ & String :: from_str ( & env , "bob" ) ,
16621701 & token,
16631702 & 500 ,
16641703 & String :: from_str ( & env, "hi" ) ,
@@ -1679,20 +1718,20 @@ mod tests {
16791718 assert ! ( found, "SocialPaymentEvent not emitted" ) ;
16801719 }
16811720
1682- // ── SC-042: registry configured but receiver never registered a username ──
1721+ // ── SC-042: registry configured but sender never registered a username ── ──
16831722 #[ test]
16841723 fn test_pay_event_username_defaults_to_empty_for_unregistered_participant ( ) {
16851724 let ( env, client, admin, _treasury, sender, receiver, registry_id) = setup_with_registry ( ) ;
16861725 let token = mint_token ( & env, & admin, & sender, 10_000 ) ;
16871726 client. set_naira_token ( & token) ;
16881727
16891728 let registry_client = MockUserRegistryClient :: new ( & env, & registry_id) ;
1690- registry_client . register ( & sender, & String :: from_str ( & env , "alice" ) ) ;
1691- // receiver intentionally left unregistered.
1729+ // sender intentionally left unregistered.
1730+ registry_client . register ( & receiver, & String :: from_str ( & env , "bob" ) ) ;
16921731
16931732 client. pay (
16941733 & sender,
1695- & receiver ,
1734+ & String :: from_str ( & env , "bob" ) ,
16961735 & token,
16971736 & 500 ,
16981737 & String :: from_str ( & env, "hi" ) ,
@@ -1705,8 +1744,8 @@ mod tests {
17051744 for item in events. iter ( ) {
17061745 if item. 1 . contains ( topic) {
17071746 let ev: SocialPaymentEvent = item. 2 . try_into_val ( & env) . unwrap ( ) ;
1708- assert_eq ! ( ev. sender_username, String :: from_str( & env, "alice " ) ) ;
1709- assert_eq ! ( ev. receiver_username, String :: from_str( & env, "" ) ) ;
1747+ assert_eq ! ( ev. sender_username, String :: from_str( & env, "" ) ) ;
1748+ assert_eq ! ( ev. receiver_username, String :: from_str( & env, "bob " ) ) ;
17101749 found = true ;
17111750 }
17121751 }
0 commit comments