The Swan protocol's purchase mechanism executes asset transfers before receiving payment, creating a vulnerability where buyers can acquire assets without paying by triggering payment failures after asset transfer completion.
The purchase function executes transfers in an unsafe sequence: https://github.com/Cyfrin/2024-10-swan-dria/blob/c8686b199daadcef3161980022e12b66a5304f8e/contracts/swan/Swan.sol#L276-L299
The bug occurs because:
The purchase function transfers tokens but doesn't update the buyer's round spending tracker
Without tracking, a buyer could exceed their round spending limit
Proof of Concept:
Buyer has spending limit of 100 tokens per round
Buyer purchases asset for 60 tokens
Spending not tracked
Buyer can purchase another 60 token asset
Total spent (120) exceeds limit (100) due to lack of tracking
The function transfers the asset before receiving payment, violating the Checks-Effects-Interactions pattern
During the asset transfer, the buyer could reenter the contract through callbacks
If the payment transfer fails after the asset transfer, the buyer would have received the asset without paying
The asset status is updated to Sold before ensuring payment, creating a potential state inconsistency
The correct sequence should be:
Check conditions
Take payment
Update state
Transfer asset
Emit event
Buyers can obtain assets without payment
Sellers can lose assets without compensation
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.