From af1a7568db723de60dd5d1e856a24e36a04fb01c Mon Sep 17 00:00:00 2001 From: Chibuikem Michael Ilonze Date: Thu, 26 Feb 2026 11:01:53 +0100 Subject: [PATCH] feat(contracts): settle recipient on cancel_stream --- contracts/stream_contract/src/lib.rs | 16 +++++++++++----- contracts/stream_contract/src/test.rs | 20 +++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/contracts/stream_contract/src/lib.rs b/contracts/stream_contract/src/lib.rs index bb00ccd..a103140 100644 --- a/contracts/stream_contract/src/lib.rs +++ b/contracts/stream_contract/src/lib.rs @@ -329,15 +329,21 @@ impl StreamContract { let now = env.ledger().timestamp(); let accrued_amount = Self::calculate_claimable(&stream, now); - // Refund only the unspent balance minus accrued tokens (accrued tokens stay for recipient) + let token_client = token::Client::new(&env, &stream.token_address); + let contract_address = env.current_contract_address(); + + // Settle recipient immediately with all final claimable amount at cancellation. + if accrued_amount > 0 { + token_client.transfer(&contract_address, &stream.recipient, &accrued_amount); + stream.withdrawn_amount = stream.withdrawn_amount.saturating_add(accrued_amount); + } + + // Refund remaining unspent balance after recipient settlement. let refunded_amount = stream .deposited_amount - .saturating_sub(stream.withdrawn_amount) - .saturating_sub(accrued_amount); + .saturating_sub(stream.withdrawn_amount); if refunded_amount > 0 { - let token_client = token::Client::new(&env, &stream.token_address); - let contract_address = env.current_contract_address(); token_client.transfer(&contract_address, &sender, &refunded_amount); } diff --git a/contracts/stream_contract/src/test.rs b/contracts/stream_contract/src/test.rs index 2ea682a..ba2279c 100644 --- a/contracts/stream_contract/src/test.rs +++ b/contracts/stream_contract/src/test.rs @@ -872,20 +872,23 @@ fn test_cancel_stream_refunds_sender() { l.timestamp += 300; }); - // Cancel stream: should refund 700 tokens to sender (1000 - 300 accrued) + // Cancel stream: should pay 300 to recipient and refund 700 to sender client.cancel_stream(&sender, &stream_id); let sender_balance_after = token_client.balance(&sender); let contract_balance_after = token_client.balance(&contract_id); + let recipient_balance_after = token_client.balance(&recipient); // Sender should receive 700 tokens back assert_eq!(sender_balance_after - sender_balance_before, 700); - // Contract should have 300 tokens remaining (for recipient to withdraw) - assert_eq!(contract_balance_after, 300); + // Recipient should receive final claimable 300 immediately + assert_eq!(recipient_balance_after, 300); + // Contract should be fully drained + assert_eq!(contract_balance_after, 0); let stream = client.get_stream(&stream_id).unwrap(); assert!(!stream.is_active); - assert_eq!(stream.withdrawn_amount, 0); // Recipient hasn't withdrawn yet + assert_eq!(stream.withdrawn_amount, 300); } #[test] @@ -921,14 +924,17 @@ fn test_cancel_stream_after_partial_withdrawal() { l.timestamp += 100; }); - // Cancel stream: should refund 700 tokens to sender (1000 - 200 withdrawn - 100 accrued) + // Cancel stream: should pay final 100 to recipient and refund 700 to sender client.cancel_stream(&sender, &stream_id); let sender_balance_after = token_client.balance(&sender); let contract_balance_after = token_client.balance(&contract_id); + let recipient_balance_after = token_client.balance(&recipient); // Sender should receive 700 tokens back assert_eq!(sender_balance_after - sender_balance_before, 700); - // Contract should have 100 tokens remaining (for recipient to withdraw) - assert_eq!(contract_balance_after, 100); + // Recipient should now hold total 300 (200 withdrawn earlier + 100 settled at cancel) + assert_eq!(recipient_balance_after, 300); + // Contract should be fully drained + assert_eq!(contract_balance_after, 0); }