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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ package version is bumped.
- **[#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.

- **[#84] Reject self-transfer in `transfer` to match `transfer_from`.**
`transfer_from` already rejected `from == to` (see #67 above), but `transfer`
had no equivalent guard: a self-transfer silently succeeded as a no-op while
still consuming the caller's `require_auth()` signature — the same security-
model inconsistency #67 was fixed to prevent, just on the other entry point.
`transfer` now panics with `"token: cannot transfer to self"` when
`from == to`, matching `transfer_from`'s existing message exactly.

#### `reward-engine`

- **[#71] Make `approve_proof` and the approve path of `resolve_dispute`
Expand Down
30 changes: 29 additions & 1 deletion contracts/eco-token/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ impl TokenContract {
/// # Panics
///
/// * Panics if `amount <= 0`
/// * Panics if `from == to`
/// * Panics if `from` has insufficient balance
///
/// # Auth
Expand All @@ -200,6 +201,10 @@ impl TokenContract {
panic!("token: amount must be positive");
}

if from == to {
panic!("token: cannot transfer to self");
}

let from_balance = storage::read_balance(&e, &from);
if from_balance < amount {
panic!("token: insufficient balance");
Expand Down Expand Up @@ -1525,6 +1530,7 @@ mod test {
}

#[test]
#[should_panic(expected = "token: cannot transfer to self")]
fn test_transfer_to_self() {
let e = Env::default();
let admin = Address::generate(&e);
Expand All @@ -1542,8 +1548,30 @@ mod test {
e.mock_all_auths();
client.mint(&user, &1000);
client.transfer(&user, &user, &500);
}

assert_eq!(client.balance(&user), 1000);
#[test]
fn test_transfer_valid_addresses_still_works() {
let e = Env::default();
let admin = Address::generate(&e);
let from = Address::generate(&e);
let to = 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(&from, &1000);
client.transfer(&from, &to, &400);

assert_eq!(client.balance(&from), 600);
assert_eq!(client.balance(&to), 400);
}

#[test]
Expand Down