From ccd961f4075af2d3ce8dbd80a6d3fffeaf8a07e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Calfeedrips=E2=80=9D?= <“garbaalfred4@gmail.com”> Date: Wed, 19 Aug 2026 14:54:44 +0100 Subject: [PATCH 1/4] fix: publish BatchExecutedEvent in execute_batch The BatchExecutedEvent struct was being constructed as a bare expression and immediately dropped, so no event was ever emitted to the ledger. Add .publish(&env) call so the event reaches the Soroban event stream. --- contracts/bulk_payment/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/bulk_payment/src/lib.rs b/contracts/bulk_payment/src/lib.rs index 954b3668..67e7033e 100644 --- a/contracts/bulk_payment/src/lib.rs +++ b/contracts/bulk_payment/src/lib.rs @@ -158,7 +158,7 @@ impl BulkPaymentContract { status: soroban_sdk::symbol_short!("completed"), }); - BatchExecutedEvent { batch_id, total_sent: total }; + BatchExecutedEvent { batch_id, total_sent: total }.publish(&env); Ok(batch_id) } From f6e667faba8381655f7708b0f38324a5d0289cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Calfeedrips=E2=80=9D?= <“garbaalfred4@gmail.com”> Date: Wed, 19 Aug 2026 21:09:44 +0100 Subject: [PATCH 2/4] fix: publish PaymentSentEvent and PaymentSkippedEvent in execute_batch_partial Both per-payment event structs were constructed and immediately dropped inside the partial batch loop. Add .publish(&env) calls so each individual payment outcome is emitted to the ledger. --- contracts/bulk_payment/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/bulk_payment/src/lib.rs b/contracts/bulk_payment/src/lib.rs index 67e7033e..df8b9bdb 100644 --- a/contracts/bulk_payment/src/lib.rs +++ b/contracts/bulk_payment/src/lib.rs @@ -214,7 +214,7 @@ impl BulkPaymentContract { recipient: op.recipient.clone(), amount: op.amount, } - ; + .publish(&env); continue; } token_client.transfer(&contract_addr, &op.recipient, &op.amount); @@ -225,7 +225,7 @@ impl BulkPaymentContract { recipient: op.recipient.clone(), amount: op.amount, } - ; + .publish(&env); } if remaining > 0 { From 6a9252aa23565f9c677a5662c778220d7e3bad72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Calfeedrips=E2=80=9D?= <“garbaalfred4@gmail.com”> Date: Thu, 20 Aug 2026 02:39:44 +0100 Subject: [PATCH 3/4] fix: publish BatchPartialEvent in execute_batch_partial The BatchPartialEvent struct was constructed as a bare expression and immediately dropped at the end of execute_batch_partial. Add .publish(&env) so the batch summary event reaches the ledger. --- contracts/bulk_payment/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/bulk_payment/src/lib.rs b/contracts/bulk_payment/src/lib.rs index df8b9bdb..84eb1a15 100644 --- a/contracts/bulk_payment/src/lib.rs +++ b/contracts/bulk_payment/src/lib.rs @@ -250,7 +250,7 @@ impl BulkPaymentContract { status, }); - BatchPartialEvent { batch_id, success_count, fail_count }; + BatchPartialEvent { batch_id, success_count, fail_count }.publish(&env); Ok(batch_id) } From c6931451d53b461b5525ce349a6402d578ead4d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Calfeedrips=E2=80=9D?= <“garbaalfred4@gmail.com”> Date: Thu, 20 Aug 2026 09:04:44 +0100 Subject: [PATCH 4/4] test: assert contract event emission for bulk payment Add two tests that verify the .publish(&env) calls actually emit events to the Soroban ledger: - test_execute_batch_emits_batch_executed_event: checks that execute_batch publishes a BatchExecutedEvent with the correct topic. - test_execute_batch_partial_emits_all_events: checks that execute_batch_partial publishes PaymentSentEvent, PaymentSkippedEvent, and BatchPartialEvent when some payments succeed and others are skipped. Uses env.events().all() with Symbol matching via FromVal to inspect the event topics emitted by the contract under test. --- contracts/bulk_payment/src/test.rs | 62 +++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/contracts/bulk_payment/src/test.rs b/contracts/bulk_payment/src/test.rs index 2e07d933..7940c3c7 100644 --- a/contracts/bulk_payment/src/test.rs +++ b/contracts/bulk_payment/src/test.rs @@ -1,9 +1,9 @@ #![cfg(test)] use super::*; use soroban_sdk::{ - testutils::Address as _, + testutils::{Address as _, Events}, token::{Client as TokenClient, StellarAssetClient}, - Address, Env, Vec, + Address, Env, FromVal, Symbol, Vec, }; // ── Errors map ──────────────────────────────────────────────────────────────── @@ -225,4 +225,62 @@ fn test_partial_batch_empty_panics() { fn test_get_batch_not_found_panics() { let (_, _, _, client) = setup(); client.get_batch(&999); +} + +// ── Event emission ──────────────────────────────────────────────────────────── + +fn has_event(env: &Env, contract_addr: &Address, event_name: &str) -> bool { + let target_sym = Symbol::new(env, event_name); + env.events().all().iter().any(|(addr, topics, _data)| { + if addr != *contract_addr { + return false; + } + topics.iter().any(|t| { + let sym = Symbol::from_val(env, &t); + sym == target_sym + }) + }) +} + +#[test] +fn test_execute_batch_emits_batch_executed_event() { + let (env, sender, token, client) = setup(); + let r1 = Address::generate(&env); + + let mut payments: Vec = Vec::new(&env); + payments.push_back(PaymentOp { recipient: r1.clone(), amount: 100 }); + + client.execute_batch(&sender, &token, &payments, &client.get_sequence()); + + assert!( + has_event(&env, &client.address, "batch_executed_event"), + "BatchExecutedEvent was not emitted" + ); +} + +#[test] +fn test_execute_batch_partial_emits_all_events() { + let (env, sender, token, client) = setup_with_sender_balance(500); + + let r1 = Address::generate(&env); + let r2 = Address::generate(&env); + + let mut payments: Vec = Vec::new(&env); + payments.push_back(PaymentOp { recipient: r1.clone(), amount: 500 }); + payments.push_back(PaymentOp { recipient: r2.clone(), amount: 400 }); + + client.execute_batch_partial(&sender, &token, &payments, &client.get_sequence()); + + assert!( + has_event(&env, &client.address, "payment_sent_event"), + "PaymentSentEvent was not emitted" + ); + assert!( + has_event(&env, &client.address, "payment_skipped_event"), + "PaymentSkippedEvent was not emitted" + ); + assert!( + has_event(&env, &client.address, "batch_partial_event"), + "BatchPartialEvent was not emitted" + ); } \ No newline at end of file