Skip to content

Commit

Permalink
covered the stablecoin collapse usecase. Unit test passing, Invariant…
Browse files Browse the repository at this point in the history
… Test reverting
  • Loading branch information
Shivendra Singh committed Sep 14, 2023
1 parent 766eee3 commit 9e82347
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 45 deletions.
93 changes: 56 additions & 37 deletions src/DSCEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {DecentralizedStableCoin} from "./DecentralizedStableCoin.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {OracleLib, AggregatorV3Interface} from "./libraries/OracleLib.sol";
import {console} from "forge-std/console.sol";

contract DSCEngine is ReentrancyGuard {
//////////////////////
Expand All @@ -61,6 +62,7 @@ contract DSCEngine is ReentrancyGuard {
error DSCEngine__HealthFactorOk(address user);
error DSCEngine__HealthFactorNotImproved(address user);
error DSCEngine__DSCNotCollapsed(uint256 totalDscMinted, uint256 totalCollateralValueOfProtocol);
error DSCEngine__DSCProtocolCollapsed(uint256 totalDscMinted, uint256 _getTotalCollateralValueOfProtocol);

///////////////////
//// Types ////
Expand Down Expand Up @@ -122,6 +124,17 @@ contract DSCEngine is ReentrancyGuard {
_;
}

modifier checkProtocolCollapsed() {
uint256 totalCollateralValueOfProtocol = _getTotalCollateralValueOfProtocol();
uint256 totalDSCMinted = i_dsc.totalSupply();

if(totalCollateralValueOfProtocol < totalDSCMinted) {
collapseDsc();
} else {
_;
}
}

///////////////////////////
/// External Functions ///
//////////////////////////
Expand Down Expand Up @@ -154,7 +167,7 @@ contract DSCEngine is ReentrancyGuard {
address tokenCollateralAddress,
uint256 amountCollateral,
uint256 amountDscToMint
) external {
) external checkProtocolCollapsed() {
depositCollateral(tokenCollateralAddress, amountCollateral);
mintDSC(amountDscToMint);
}
Expand Down Expand Up @@ -227,7 +240,7 @@ contract DSCEngine is ReentrancyGuard {

function mintDSC(
uint256 amountDscToMint
) public moreThanZero(amountDscToMint) {
) public moreThanZero(amountDscToMint) checkProtocolCollapsed(){
s_DSCMinted[msg.sender] += amountDscToMint;

// Check if Collateral value is more than DSC value
Expand Down Expand Up @@ -268,7 +281,7 @@ contract DSCEngine is ReentrancyGuard {
address collateralToken,
address user,
uint256 debtToCover
) external {
) external checkProtocolCollapsed(){
uint256 startingHealthFactor = _healthFactor(user);

if (startingHealthFactor >= MIN_HEALTH_FACTOR) {
Expand Down Expand Up @@ -401,6 +414,45 @@ contract DSCEngine is ReentrancyGuard {
return totalCollateralValueInUsd;
}


/*
* @title CollapseDSC
* @author Shivendra Singh
* @notice In case of total collateral value being MORE than the DSC Minted, the * protocol should render itself as collapsed. In such an event, the protocol *will burn all minted DSC and redeem all deposited collateral of each and every * user.
*/

function collapseDsc() public {

uint256 totalCollateralValueOfProtocol = _getTotalCollateralValueOfProtocol();
uint256 totalDSCMinted = i_dsc.totalSupply();


if(totalCollateralValueOfProtocol > totalDSCMinted) {
revert DSCEngine__DSCNotCollapsed(totalDSCMinted, totalCollateralValueOfProtocol);
}

emit DSCProtocolCollapsed();
console.log('Users in the protocol: ', s_users.length);

for(uint256 u = 0; u < s_users.length; u++){
address user = s_users[u];
uint256 userDscMinted = s_DSCMinted[user];

if(userDscMinted > 0) {
_burnDSC(userDscMinted, user);
}

for(uint256 t = 0 ; t < s_collateralTokens.length; t++) {
address collateralToken = s_collateralTokens[t];
uint256 collateralTokenAmount = s_collateralDeposited[user][collateralToken];

if(collateralTokenAmount > 0 ) {
_redeemCollateral(collateralToken, collateralTokenAmount, user, user);
}
}
}
}

//////////////////////////
/// Internal Functions ///
//////////////////////////
Expand All @@ -415,6 +467,7 @@ contract DSCEngine is ReentrancyGuard {
if (!success) {
revert DSCEngine__TransferFailed();
}

i_dsc.burn(amountToBurn);
emit DSCBurnt(user, amountToBurn);
}
Expand Down Expand Up @@ -466,40 +519,6 @@ contract DSCEngine is ReentrancyGuard {
return (dscMinted, collateralDepositedValue);
}


/*
* @title CollapseDSC
* @author Shivendra Singh
* @notice In case of total collateral value being MORE than the DSC Minted, the * protocol should render itself as collapsed. In such an event, the protocol *will burn all minted DSC and redeem all deposited collateral of each and every * user.
*/

function _collapseDsc() internal {
uint256 totalCollateralValueOfProtocol = _getTotalCollateralValueOfProtocol();
uint256 totalDSCMinted = i_dsc.totalSupply();

if(totalCollateralValueOfProtocol > totalDSCMinted) {
revert DSCEngine__DSCNotCollapsed(totalDSCMinted, totalCollateralValueOfProtocol);
}

for(uint256 u = 0; u < s_users.length; u++){
address user = s_users[u];
uint256 usersTotalDscMinted = s_DSCMinted[user];

_burnDSC(usersTotalDscMinted, user);

for(uint256 t = 0 ; t < s_collateralTokens.length; t++){
address collateralToken = s_collateralTokens[t];
uint256 collateralTokenAmount = s_collateralDeposited[user][collateralToken];

if(collateralTokenAmount > 0 ) {
_redeemCollateral(collateralToken, collateralTokenAmount, address(this), user);
}
}
}

emit DSCProtocolCollapsed();
}

function _getTotalCollateralValueOfProtocol() internal view returns (uint256) {
uint256 totalCollateralValueOfProtocol;

Expand Down
27 changes: 21 additions & 6 deletions test/fuzz/Handler.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract Handler is Test {
vm.startPrank(msg.sender);
ERC20Mock(collateralToken).mint(msg.sender, collateralAmount);

// giving approve to dscEngine to deposit tokens as collateral
// giving approve to dscEngine to deposit/transfer collateral tokens if required
ERC20Mock(collateralToken).approve(
address(dscEngine),
collateralAmount
Expand Down Expand Up @@ -116,20 +116,35 @@ contract Handler is Test {
return;
}

vm.prank(sender);
vm.startPrank(sender);
dscEngine.mintDSC(mintAmount);
dsc.approve(address(dscEngine), mintAmount); // dsc approval given to dscEngine by the user, incase burning of these tokens required.
vm.stopPrank();
mintDscCalls++;
}

/**
* TODO This breaks our system and should be taken care off.

// TODO This breaks our system and should be taken care off.
function updateEthUsdPriceFeed(uint96 newEthUsdPrice) public {

// Arrange
MockV3Aggregator ethUsdPriceFeed = MockV3Aggregator(dscEngine.getCollateralTokenPriceFeed(wEth));
int256 newPrice = int256(uint256(newEthUsdPrice));
ethUsdPriceFeed.updateAnswer(newPrice);


uint256 totalDscMinted = dsc.totalSupply();
uint256 wEthDeposited = ERC20Mock(wEth).balanceOf(address(dscEngine));
uint256 wBtcDeposited = ERC20Mock(wBtc).balanceOf(address(dscEngine));

uint256 totalCollateralValue = dscEngine.getUsdValue(wEth, wEthDeposited) + dscEngine.getUsdValue(wBtc, wBtcDeposited);

// Act
if(totalCollateralValue < totalDscMinted){
dscEngine.collapseDsc();
}
}
*/


////////////////////
/// Helper Func. ///
Expand Down
37 changes: 35 additions & 2 deletions test/unit/DSCEngineTest.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

Expand Down Expand Up @@ -401,7 +401,6 @@ contract DSCEngineTest is Test {
function testBurnDsc() public depositedCollateralAndMintedDSC {
// Arrange
vm.startPrank(user);

dsc.approve(address(dscEngine), mintAmount);
dscEngine.burnDSC(mintAmount);
vm.stopPrank();
Expand Down Expand Up @@ -495,4 +494,38 @@ contract DSCEngineTest is Test {
console.log("Liquidator ETH Bal: ", actualEthBalOfLiquidator);
assertEq(actualEthBalOfLiquidator, expectedEthBal);
}

///////////////////////////////
/// Collapse Protocol tests ///
///////////////////////////////

function testRevertIfProtocolNotCollapsed() public depositedCollateralAndMintedDSC {
vm.expectRevert(abi.encodeWithSelector(
DSCEngine.DSCEngine__DSCNotCollapsed.selector,
mintAmount,
dscEngine.getUsdValue(weth, COLLATERAL_AMOUNT)
)
);
dscEngine.collapseDsc();
}

function testProtocolCollapse() public depositedCollateralAndMintedDSC {
// Arrange
// Crash the collateralValue
int256 crashedEthUsdPrice = 8e8; // $8/ETH, collateral value = 10 ether * $8 = 80 < 100 (mintAmount)
MockV3Aggregator ethUsdPriceFeedAggregator = MockV3Aggregator(wethUsdPriceFeed);
ethUsdPriceFeedAggregator.updateAnswer(crashedEthUsdPrice);

vm.startPrank(user);
dsc.approve(address(dscEngine), mintAmount);

// Act
// dscEngine.collapseDsc();
dscEngine.mintDSC(mintAmount);

// Assert
assertEq(dsc.totalSupply(), 0);
assertEq(dscEngine.getCollateralDeposited(weth), 0);
vm.stopPrank();
}
}

0 comments on commit 9e82347

Please sign in to comment.