Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: medium
Invalid

Withdrawing BuyerAgent money should prevent `totalFees` from withdrawing for purchase requests in Round

Description

BuyerAgent is designed to buy items listed to him in Sell phase, and in Buy phase he will buy it.

Since Sellers will list on Sell phase, there is a check that prevents Buyers from taking the maximum money they can use in the Buying Process. To not make Buyers grief Sellers buy withdrawing the fees they took.

If the BuyerAgent may a purchase request for this Round, it should make an update request too in that Round under Normal Situations.

swan contest codehawks

So in case of a successful purchase request, we should make sure there is oracleFee values before withdrawing, to not alter the updating state process. But this is not implemented in the code.

swan/BuyerAgent.sol#L262-L277

function withdraw(uint96 _amount) public onlyAuthorized {
...
>> if (phase != Phase.Withdraw) {
// instead of checking `treasury - _amount < minFoundAmount`
// we check this way to prevent underflows
if (treasury() < minFundAmount() + _amount) {
revert MinFundSubceeded(_amount);
}
}
// transfer the tokens to the owner of Buyer
swan.token().transfer(owner(), _amount);
}

This will result in reverting when requesting an update state request by oracles in case of withdrawing money at this round (the round we bought items in), as we will make an update state request too which also requires fees.

Recommendations

Check that the amount to withdraw + fees doesn't exceed the balance of the contract when withdrawing money in Withdraw phase in an active purchase round.

diff --git a/contracts/swan/BuyerAgent.sol b/contracts/swan/BuyerAgent.sol
index 6660a67..f214601 100644
--- a/contracts/swan/BuyerAgent.sol
+++ b/contracts/swan/BuyerAgent.sol
@@ -260,7 +260,7 @@ contract BuyerAgent is Ownable {
/// @dev If the current phase is `Withdraw` buyer can withdraw any amount of tokens.
/// @dev If the current phase is not `Withdraw` buyer has to leave at least `minFundAmount` in the contract.
function withdraw(uint96 _amount) public onlyAuthorized {
- (, Phase phase,) = getRoundPhase();
+ (uint256 round, Phase phase,) = getRoundPhase();
// if we are not in Withdraw phase, we must leave
// at least minFundAmount in the contract
if (phase != Phase.Withdraw) {
// instead of checking `treasury - _amount < minFoundAmount`
// we check this way to prevent underflows
if (treasury() < minFundAmount() + _amount) {
revert MinFundSubceeded(_amount);
}
}
+ if (phase == Phase.Withdraw && isOracleRequestProcessed[oraclePurchaseRequests[round]]) {
+ if (treasury() < swan.getOracleFee() + _amount) {
+ revert MinFundSubceeded(_amount);
+ }
+ }
+
// transfer the tokens to the owner of Buyer
swan.token().transfer(owner(), _amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Appeal created

alqaqa Submitter
8 months ago
inallhonesty Lead Judge
7 months ago
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.