Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
40 changes: 40 additions & 0 deletions contracts/eco-token/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Val> = vec![
&e,
Symbol::new(&e, "burn_event").to_val(),
user.clone().into_val(&e),
];
let expected_data: Val = soroban_sdk::Map::<Symbol, Val>::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() {
Expand Down