Critical Smart Contract Bug: `payout.distribute_prize()` Writes Idempotency Key After Transfers, Enabling Double-Payment
A critical vulnerability has been identified in a smart contract's payout function, where the idempotency guard is written *after* token transfers are executed. This flaw violates the fundamental Checks-Effects-Interactions (CEI) pattern, creating a direct path for double payments and fund loss. Specifically, in the `distribute_prize()` function within `contract/payout/src/lib.rs`, the code performs transfers to all winners in a loop and only then writes the storage key that should prevent re-execution.
The bug's mechanics are straightforward but dangerous. The function first checks if a `PrizePayout` key exists for a given game ID. If not, it proceeds to transfer tokens to each winner. However, if any single transfer in that loop fails—for instance, due to a recipient's account being unfunded or a temporary network issue—the transaction will revert. Crucially, because the idempotency key is set at the very end, the failed transaction leaves no record of the attempted payout. A subsequent retry of the same function would pass the initial guard check again, initiating a second round of transfers to any winners who received funds in the first, failed attempt.
This design flaw exposes the contract to significant financial risk. It undermines the core guarantee of idempotency, turning a routine error into a potential vector for draining funds. The vulnerability is a classic example of improper state management in smart contract development, where the order of operations is not just an optimization but a security requirement. The fix requires moving the idempotency key write to occur immediately after validation and before any external interactions, ensuring state is updated prior to any irreversible actions.