diff --git a/CHANGELOG.md b/CHANGELOG.md index 315f0b2..4ce2622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ package version is bumped. #### `eco-token` +- **[#85] Index `from` address in `BurnEvent` topic schema.** + `BurnEvent.from` is marked `#[topic]`, ensuring on-chain burn history can be filtered by address, matching the topic indexing conventions of `TransferEvent`, `MintEvent`, and `ApproveEvent`. Added `test_burn_emits_event_with_indexed_from` verifying topic vector layout and payload schema. + - **[#67] Prevent self-transfer allowance drain in `transfer_from` and fix storage re-fetch TOCTOU in `spend_allowance`.** `transfer_from` now panics with `"token: cannot transfer to self"` when `from == to`, preventing spenders from burning an owner's allowance without transferring tokens. `spend_allowance` in `storage.rs` now accepts the `&Allowance` struct directly instead of re-fetching from persistent storage with `.unwrap()`, eliminating a potential TOCTOU window. diff --git a/contracts/eco-token/src/token.rs b/contracts/eco-token/src/token.rs index cd775d8..d9bf0e5 100644 --- a/contracts/eco-token/src/token.rs +++ b/contracts/eco-token/src/token.rs @@ -1142,6 +1142,46 @@ mod test { assert_eq!(client.total_supply(), 600); } + #[test] + fn test_burn_emits_event_with_indexed_from() { + use soroban_sdk::testutils::Events as _; + use soroban_sdk::vec; + use soroban_sdk::{IntoVal, Symbol, Val}; + let e = Env::default(); + let admin = Address::generate(&e); + let user = Address::generate(&e); + let contract_id = e.register(TokenContract, ()); + let client = TokenContractClient::new(&e, &contract_id); + + client.initialize( + &admin, + &String::from_str(&e, "ECO"), + &String::from_str(&e, "ECO"), + &7, + ); + + e.mock_all_auths(); + client.mint(&user, &1000); + client.burn(&user, &400); + + let events = e.events().all(); + let expected_topics: soroban_sdk::Vec = vec![ + &e, + Symbol::new(&e, "burn_event").to_val(), + user.clone().into_val(&e), + ]; + let expected_data: Val = soroban_sdk::Map::::from_array( + &e, + [(Symbol::new(&e, "amount"), 400i128.into_val(&e))], + ) + .into_val(&e); + + assert_eq!( + events, + vec![&e, (contract_id, expected_topics, expected_data)] + ); + } + #[test] #[should_panic(expected = "token: insufficient balance")] fn test_burn_more_than_balance() {