forked from The-Web3-Compass/30-days-of-solidity-submissions
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDay29simplestablecoin.sol
More file actions
118 lines (91 loc) · 5.23 KB
/
Day29simplestablecoin.sol
File metadata and controls
118 lines (91 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract SimpleStablecoin is ERC20, Ownable, ReentrancyGuard, AccessControl {
using SafeERC20 for IERC20;
bytes32 public constant PRICE_FEED_MANAGER_ROLE = keccak256("PRICE_FEED_MANAGER_ROLE");
IERC20 public immutable collateralToken;
uint8 public immutable collateralDecimals;
AggregatorV3Interface public priceFeed;
uint256 public collateralizationRatio = 150; // Expressed as a percentage (150 = 150%)
event Minted(address indexed user, uint256 amount, uint256 collateralDeposited);
event Redeemed(address indexed user, uint256 amount, uint256 collateralReturned);
event PriceFeedUpdated(address newPriceFeed);
event CollateralizationRatioUpdated(uint256 newRatio);
error InvalidCollateralTokenAddress();
error InvalidPriceFeedAddress();
error MintAmountIsZero();
error InsufficientStablecoinBalance();
error CollateralizationRatioTooLow();
constructor(
address _collateralToken,
address _initialOwner,
address _priceFeed
) ERC20("Simple USD Stablecoin", "sUSD") Ownable(_initialOwner) {
if (_collateralToken == address(0)) revert InvalidCollateralTokenAddress();
if (_priceFeed == address(0)) revert InvalidPriceFeedAddress();
collateralToken = IERC20(_collateralToken);
collateralDecimals = IERC20Metadata(_collateralToken).decimals();
priceFeed = AggregatorV3Interface(_priceFeed);
_grantRole(DEFAULT_ADMIN_ROLE, _initialOwner);
_grantRole(PRICE_FEED_MANAGER_ROLE, _initialOwner);
}
function getCurrentPrice() public view returns (uint256) {
(, int256 price, , , ) = priceFeed.latestRoundData();
require(price > 0, "Invalid price feed response");
return uint256(price);
}
function mint(uint256 amount) external nonReentrant {
if (amount == 0) revert MintAmountIsZero();
uint256 collateralPrice = getCurrentPrice();
uint256 requiredCollateralValueUSD = amount * (10 ** decimals()); // 18 decimals assumed for sUSD
uint256 requiredCollateral = (requiredCollateralValueUSD * collateralizationRatio) / (100 * collateralPrice);
uint256 adjustedRequiredCollateral = (requiredCollateral * (10 ** collateralDecimals)) / (10 ** priceFeed.decimals());
collateralToken.safeTransferFrom(msg.sender, address(this), adjustedRequiredCollateral);
_mint(msg.sender, amount);
emit Minted(msg.sender, amount, adjustedRequiredCollateral);
}
function redeem(uint256 amount) external nonReentrant {
if (amount == 0) revert MintAmountIsZero();
if (balanceOf(msg.sender) < amount) revert InsufficientStablecoinBalance();
uint256 collateralPrice = getCurrentPrice();
uint256 stablecoinValueUSD = amount * (10 ** decimals());
uint256 collateralToReturn = (stablecoinValueUSD * 100) / (collateralizationRatio * collateralPrice);
uint256 adjustedCollateralToReturn = (collateralToReturn * (10 ** collateralDecimals)) / (10 ** priceFeed.decimals());
_burn(msg.sender, amount);
collateralToken.safeTransfer(msg.sender, adjustedCollateralToReturn);
emit Redeemed(msg.sender, amount, adjustedCollateralToReturn);
}
function setCollateralizationRatio(uint256 newRatio) external onlyOwner {
if (newRatio < 100) revert CollateralizationRatioTooLow();
collateralizationRatio = newRatio;
emit CollateralizationRatioUpdated(newRatio);
}
function setPriceFeedContract(address _newPriceFeed) external onlyRole(PRICE_FEED_MANAGER_ROLE) {
if (_newPriceFeed == address(0)) revert InvalidPriceFeedAddress();
priceFeed = AggregatorV3Interface(_newPriceFeed);
emit PriceFeedUpdated(_newPriceFeed);
}
function getRequiredCollateralForMint(uint256 amount) public view returns (uint256) {
if (amount == 0) return 0;
uint256 collateralPrice = getCurrentPrice();
uint256 requiredCollateralValueUSD = amount * (10 ** decimals());
uint256 requiredCollateral = (requiredCollateralValueUSD * collateralizationRatio) / (100 * collateralPrice);
uint256 adjustedRequiredCollateral = (requiredCollateral * (10 ** collateralDecimals)) / (10 ** priceFeed.decimals());
return adjustedRequiredCollateral;
}
function getCollateralForRedeem(uint256 amount) public view returns (uint256) {
if (amount == 0) return 0;
uint256 collateralPrice = getCurrentPrice();
uint256 stablecoinValueUSD = amount * (10 ** decimals());
uint256 collateralToReturn = (stablecoinValueUSD * 100) / (collateralizationRatio * collateralPrice);
uint256 adjustedCollateralToReturn = (collateralToReturn * (10 ** collateralDecimals)) / (10 ** priceFeed.decimals());
return adjustedCollateralToReturn;
}
}