Problem
contracts/payment/src/lib.rs:190-243 - release_payment() performs two token transfers BEFORE updating the payment status:
token_client.transfer(..., &payment.restaurant_wallet, &net_amount); // external call
token_client.transfer(..., &treasury, &payment.fee_amount); // external call
payment.status = PaymentStatus::Released; // state update LAST
This violates the Checks-Effects-Interactions pattern. If the token contract triggers re-entry during transfer, a second release_payment() call could execute with status still set to Escrowed, draining the escrow twice.
Proposed Solution
Reorder to Checks-Effects-Interactions:
- Validate all preconditions (status == Escrowed, caller auth) - CHECKS
- Set
payment.status = PaymentStatus::Released and persist to storage - EFFECTS
- Perform both
token_client.transfer() calls - INTERACTIONS
Add a releasing: bool reentrancy guard to storage that panics if already set on entry.
Acceptance Criteria
Problem
contracts/payment/src/lib.rs:190-243-release_payment()performs two token transfers BEFORE updating the payment status:This violates the Checks-Effects-Interactions pattern. If the token contract triggers re-entry during transfer, a second
release_payment()call could execute with status still set toEscrowed, draining the escrow twice.Proposed Solution
Reorder to Checks-Effects-Interactions:
payment.status = PaymentStatus::Releasedand persist to storage - EFFECTStoken_client.transfer()calls - INTERACTIONSAdd a
releasing: boolreentrancy guard to storage that panics if already set on entry.Acceptance Criteria
payment.statusis persisted to storage BEFORE the firsttoken_client.transfer()callrelease_payment()duringtransfer()is rejected by the reentrancy guard