diff --git a/src/DSCEngine.sol b/src/DSCEngine.sol index 8d8b717..863a712 100644 --- a/src/DSCEngine.sol +++ b/src/DSCEngine.sol @@ -29,12 +29,15 @@ contract DSCEngine is ReentrancyGuard { error DSCEngine__TransferFaild(); error DSCEngine__MintFaild(); error DSCEngine__BreaksHealthFactor(uint256 healthFactor); + error DSCEngine__HealthFActorOk(); + error DSCEngine__HealtFactorNotImproved(); uint256 private constant ADDITIONAL_FEED_PRECISION = 1e10; uint256 private constant PRECISION = 1e18; uint256 private constant LIQUIDATION_THRESHOLD = 50; + uint256 private constant LIQUIDATION_BONUS = 10; uint256 private constant LIQUIDATION_PRECISION = 100; - uint256 private constant MIN_HEALTH_FACTOR = 1; + uint256 private constant MIN_HEALTH_FACTOR = 1e18; mapping(address token => address priceFeed) private s_priceFeeds; mapping(address user => mapping(address token => uint256 amount)) private s_collateralDeposited; @@ -44,7 +47,9 @@ contract DSCEngine is ReentrancyGuard { DecentralizedStableCoin private immutable i_dsc; event CollateralDeposited(address indexed user, address indexed token, uint256 indexed amount); - event CollateralRedeemed(address indexed user, address indexed token, uint256 indexed amount); + event CollateralRedeemed( + address indexed redeemedFrom, address indexed redeemedTo, address indexed token, uint256 amount + ); modifier moreThenZero(uint256 amount) { if (amount == 0) { @@ -106,13 +111,7 @@ contract DSCEngine is ReentrancyGuard { moreThenZero(amountCollateral) nonReentrant { - s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral; - emit CollateralRedeemed(msg.sender, tokenCollateralAddress, amountCollateral); - - bool success = IERC20(tokenCollateralAddress).transfer(msg.sender, amountCollateral); - if (!success) { - revert DSCEngine__TransferFaild(); - } + _redeemCollateral(tokenCollateralAddress, amountCollateral, msg.sender, msg.sender); _revertIfHealthFactoreIsBroken(msg.sender); } @@ -126,18 +125,45 @@ contract DSCEngine is ReentrancyGuard { } function burnDsc(uint256 amount) public moreThenZero(amount) { - s_DSCMinted[msg.sender] -= amount; - bool success = i_dsc.transferFrom(msg.sender, address(this), amount); - if (!success) { - revert DSCEngine__TransferFaild(); - } - i_dsc.burn(amount); + _burnDsc(amount, msg.sender, msg.sender); } - function liquidate() external {} + function liquidate(address collateral, address user, uint256 debtToCover) + external + moreThenZero(debtToCover) + nonReentrant + { + uint256 startingUserHealthFactor = _healthFactor(user); + if (startingUserHealthFactor >= MIN_HEALTH_FACTOR) { + revert DSCEngine__HealthFActorOk(); + } + + uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover); + + uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION; + uint256 totalCollateralToRedeem = tokenAmountFromDebtCovered + bonusCollateral; + _redeemCollateral(collateral, totalCollateralToRedeem, user, msg.sender); + _burnDsc(debtToCover, user, msg.sender); + + uint256 endingUserHealthFactor = _healthFactor(user); + + if (endingUserHealthFactor <= startingUserHealthFactor) { + revert DSCEngine__HealtFactorNotImproved(); + } + _revertIfHealthFactoreIsBroken(msg.sender); + } function getHealthFactor() external {} + function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private { + s_DSCMinted[onBehalfOf] -= amountDscToBurn; + bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn); + if (!success) { + revert DSCEngine__TransferFaild(); + } + i_dsc.burn(amountDscToBurn); + } + function _getAccountInformation(address user) private view @@ -160,6 +186,17 @@ contract DSCEngine is ReentrancyGuard { } } + function _redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral, address from, address to) + private + { + s_collateralDeposited[from][tokenCollateralAddress] += amountCollateral; + emit CollateralRedeemed(from, to, tokenCollateralAddress, amountCollateral); + bool success = IERC20(tokenCollateralAddress).transfer(to, amountCollateral); + if (!success) { + revert DSCEngine__TransferFaild(); + } + } + function getAccountCollateralValue(address user) public view returns (uint256 totalCollateralValueInUsd) { for (uint256 i = 0; i < s_collateralTokens.length; i++) { address token = s_collateralTokens[i]; @@ -169,6 +206,12 @@ contract DSCEngine is ReentrancyGuard { return totalCollateralValueInUsd; } + function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) { + AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]); + (, int256 price,,,) = priceFeed.latestRoundData(); + return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION); + } + function getUsdValue(address token, uint256 amount) public view returns (uint256) { AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]); (, int256 price,,,) = priceFeed.latestRoundData();