@@ -565,10 +565,10 @@ async fn test_receive_to_onchain_with_channel() {
565565 ) ;
566566 assert_ne ! ( tx. time_since_epoch, Duration :: ZERO , "Time should be set" ) ;
567567 assert_eq ! ( tx. amount, Some ( recv_amt) , "Amount should equal received amount" ) ;
568- // fixme assert!(
569- // tx.fee.unwrap() > Amount::ZERO,
570- // "On-chain receive should have rebalance fees after channel opening"
571- // );
568+ assert ! (
569+ tx. fee. unwrap( ) > Amount :: ZERO ,
570+ "On-chain receive should have rebalance fees after channel opening"
571+ ) ;
572572
573573 // Validate fee is reasonable (should be less than 5% of received amount for rebalance)
574574 let fee_ratio = tx. fee . unwrap ( ) . milli_sats ( ) as f64 / recv_amt. milli_sats ( ) as f64 ;
@@ -894,6 +894,110 @@ async fn test_pay_onchain_from_self_custody() {
894894 . await ;
895895}
896896
897+ #[ tokio:: test( flavor = "multi_thread" ) ]
898+ async fn test_pay_onchain_from_channel ( ) {
899+ test_utils:: run_test ( |params| async move {
900+ let wallet = Arc :: clone ( & params. wallet ) ;
901+ let bitcoind = Arc :: clone ( & params. bitcoind ) ;
902+ let third_party = Arc :: clone ( & params. third_party ) ;
903+
904+ // get a channel so we can make a payment
905+ let recv = open_channel_from_lsp ( & wallet, Arc :: clone ( & third_party) ) . await ;
906+
907+ let starting_bal = wallet. get_balance ( ) . await . unwrap ( ) ;
908+ assert_eq ! (
909+ starting_bal. available_balance( ) ,
910+ recv. saturating_sub( Amount :: from_sats( 2_000 ) . unwrap( ) )
911+ ) ;
912+ assert_eq ! ( starting_bal. pending_balance, Amount :: ZERO ) ;
913+
914+ // wait for sync
915+ generate_blocks ( & bitcoind, 6 ) ;
916+ test_utils:: wait_for_condition ( "wallet sync after channel open" , || async {
917+ wallet. channels ( ) . iter ( ) . any ( |a| a. confirmations . is_some_and ( |c| c > 0 ) && a. is_usable )
918+ } )
919+ . await ;
920+
921+ // get address from third party node
922+ let addr = third_party. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
923+ let send_amount = Amount :: from_sats ( 10_000 ) . unwrap ( ) ;
924+
925+ let instr = wallet. parse_payment_instructions ( addr. to_string ( ) . as_str ( ) ) . await . unwrap ( ) ;
926+ let info = PaymentInfo :: build ( instr, Some ( send_amount) ) . unwrap ( ) ;
927+ wallet. pay ( & info) . await . unwrap ( ) ;
928+
929+ // sleep for a second to wait for proper broadcast
930+ tokio:: time:: sleep ( Duration :: from_secs ( 1 ) ) . await ;
931+
932+ // confirm the tx
933+ generate_blocks ( & bitcoind, 6 ) ;
934+
935+ // sleep for a second to wait for sync
936+ tokio:: time:: sleep ( Duration :: from_secs ( 1 ) ) . await ;
937+
938+ // wait for payment to complete
939+ test_utils:: wait_for_condition ( "on-chain payment completion" , || async {
940+ let payments = wallet. list_transactions ( ) . await . unwrap ( ) ;
941+ let payment = payments. into_iter ( ) . find ( |p| p. outbound ) ;
942+ if payment. as_ref ( ) . is_some_and ( |p| p. status == TxStatus :: Failed ) {
943+ panic ! ( "Payment failed" ) ;
944+ }
945+ payment. is_some_and ( |p| p. status == TxStatus :: Completed )
946+ } )
947+ . await ;
948+
949+ // check the payment is correct
950+ let payments = wallet. list_transactions ( ) . await . unwrap ( ) ;
951+ let payment = payments. into_iter ( ) . find ( |p| p. outbound ) . unwrap ( ) ;
952+
953+ // Comprehensive validation for outgoing on-chain payment
954+ assert_eq ! ( payment. amount, Some ( send_amount) , "Amount should equal sent amount" ) ;
955+ assert ! (
956+ payment. fee. is_some_and( |f| f > Amount :: ZERO ) ,
957+ "On-chain payment should have non-zero fees"
958+ ) ;
959+ assert ! ( payment. outbound, "Outgoing payment should be outbound" ) ;
960+ assert ! (
961+ matches!( payment. payment_type, PaymentType :: OutgoingOnChain { .. } ) ,
962+ "Payment type should be OutgoingOnChain"
963+ ) ;
964+ assert_eq ! ( payment. status, TxStatus :: Completed , "Payment should be completed" ) ;
965+ assert_ne ! ( payment. time_since_epoch, Duration :: ZERO , "Time should be set" ) ;
966+
967+ // Validate fee is reasonable for on-chain (should be less than 1% of sent amount)
968+ let fee_ratio = payment. fee . unwrap ( ) . milli_sats ( ) as f64 / send_amount. milli_sats ( ) as f64 ;
969+ assert ! (
970+ fee_ratio < 0.01 ,
971+ "On-chain fee should be less than 1% of sent amount, got {:.2}%" ,
972+ fee_ratio * 100.0
973+ ) ;
974+
975+ // Check that payment_type contains txid for completed payments
976+ if let PaymentType :: OutgoingOnChain { txid } = & payment. payment_type {
977+ assert ! ( txid. is_some( ) , "Completed on-chain payment should have txid" ) ;
978+ }
979+
980+ // check balance left our wallet
981+ let bal = wallet. get_balance ( ) . await . unwrap ( ) ;
982+ assert_eq ! (
983+ bal. pending_balance,
984+ recv. saturating_sub( send_amount) . saturating_sub( payment. fee. unwrap( ) )
985+ ) ;
986+
987+ // Wait for third party node to receive it
988+ test_utils:: wait_for_condition ( "on-chain payment received" , || async {
989+ let payments = third_party. list_payments ( ) ;
990+ payments. iter ( ) . any ( |p| {
991+ p. status == PaymentStatus :: Succeeded
992+ && p. direction == PaymentDirection :: Inbound
993+ && p. amount_msat == Some ( send_amount. milli_sats ( ) )
994+ } )
995+ } )
996+ . await ;
997+ } )
998+ . await ;
999+ }
1000+
8971001#[ tokio:: test( flavor = "multi_thread" ) ]
8981002async fn test_force_close_handling ( ) {
8991003 test_utils:: run_test ( |params| async move {
0 commit comments