Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 44 additions & 12 deletions contracts/core/stablecoin/AccessController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@
pragma solidity ^0.8.28;

// Importing the necessary libraries
import "@openzeppelin/contracts/access/Ownable.sol"; // Importing the Ownable contract from OpenZeppelin
import "@openzeppelin/contracts/access/AccessControl.sol"; // AccessControl contract from OpenZeppelin

/**
* @title AccessController
* @dev This contract manages access control for the stablecoin system.
*/
contract AccessController is Ownable {
contract AccessController is AccessControl {
// State variables of the contract
bool public mintPaused; // Flag to pause minting (0 = not paused(active), 1 = paused)
bool public burnPaused; // Flag to pause burning (0 = not paused(active), 1 = paused)
// Flag to indicate if the contract is in emergency mode (0 = not in emergency mode, 1 = in emergency mode)
bool public emergencyMode;

// Roles for access control
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); // Role for pausing
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); // Role for admin


// Events

Expand All @@ -40,10 +44,17 @@ contract AccessController is Ownable {
event EmergencyShutdown(uint256 timestamp);

// constructor to initialize the access controller
constructor() Ownable(msg.sender) {
constructor() {
mintPaused = false; // Initialize minting as not paused
burnPaused = false; // Initialize burning as not paused
emergencyMode = false; // Initialize emergency mode as not active

// Grant the deployer the default admin role
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
// Grant the deployer the pauser role
_grantRole(PAUSER_ROLE, msg.sender);
// Grant the deployer the admin role
_grantRole(ADMIN_ROLE, msg.sender);
}

/**
Expand All @@ -66,39 +77,56 @@ contract AccessController is Ownable {

/**
* @notice Checks if pausing is allowed
* @dev This function returns true if the caller is the owner
* @dev This function returns true if the caller has the pauser role
* @param caller The address of the caller
* @return bool True if pausing is allowed, false otherwise
*/
function canPause(address caller) external view returns (bool) {
return caller == owner(); // Check if the caller is the owner
// Check if the caller has the pauser or admin role
return hasRole(PAUSER_ROLE, caller) || hasRole(ADMIN_ROLE, caller);
}

/**
* @notice Checks if unpausing is allowed
* @dev This function returns true if the caller is the owner and not in emergency mode
* @dev This function returns true if the caller has the pauser role and not in emergency mode
* @param caller The address of the caller
* @return bool True if unpausing is allowed, false otherwise
*/
function canUnpause(address caller) external view returns (bool) {
return caller == owner() && !emergencyMode; // Check if the caller is the owner and not in emergency mode
// Check if the caller has the pauser role or admin role and the contract is not in emergency mode
// This prevents the pauser from unpausing the contract during emergency mode
return (hasRole(PAUSER_ROLE, caller) || hasRole(ADMIN_ROLE, caller)) && !emergencyMode;
}

/**
* @notice Checks if emergency shutdown is allowed
* @dev This function returns true if the caller is the owner
* @dev This function returns true if the caller has the ADMIN_ROLE
* @param caller The address of the caller
* @return bool True if emergency shutdown is allowed, false otherwise
*/
function canEmergencyShutdown(address caller) external view returns (bool) {
return caller == owner(); // Check if the caller is the owner
return hasRole(ADMIN_ROLE, caller); // Check if the caller has the ADMIN_ROLE
}

/**
* @notice Checks if setting parameters is allowed
* @dev This function returns true if the caller has the ADMIN_ROLE
* @param caller The address of the caller
* @return bool True if setting parameters is allowed, false otherwise
*/
function canSetParams(address caller) external view returns (bool) {
return hasRole(ADMIN_ROLE, caller); // Check if the caller has the ADMIN_ROLE
}

/**
* @dev Pause or start minting
* @param _statusMinting true to pause minting, false to start minting
*/
function setMintPaused(bool _statusMinting) external onlyOwner {
function setMintPaused(bool _statusMinting) external {
// Check if the caller has the pauser role or admin role
require(hasRole(PAUSER_ROLE, msg.sender) || hasRole(ADMIN_ROLE, msg.sender),
"Caller must have pauser or admin role");

// Check if the new minting status is different from the current minting status
// This prevents unnecessary updates which could lead to gas wastage
require(_statusMinting != mintPaused, "New minting status must be different from current minting status");
Expand All @@ -110,7 +138,11 @@ contract AccessController is Ownable {
* @dev Pause or start burning
* @param _statusBurning true to pause burning, false to start burning
*/
function setBurnPaused(bool _statusBurning) external onlyOwner {
function setBurnPaused(bool _statusBurning) external {
// Check if the caller has the pauser role or admin role
require(hasRole(PAUSER_ROLE, msg.sender) || hasRole(ADMIN_ROLE, msg.sender),
"Caller must have pauser or admin role");

// Check if the new burning status is different from the current burning status
// This prevents unnecessary updates which could lead to gas wastage
require(_statusBurning != burnPaused, "New burning status must be different from current burning status");
Expand All @@ -122,7 +154,7 @@ contract AccessController is Ownable {
* @dev Set the emergency mode status
* @param _statusEmergency true to set emergency mode, false to unset emergency mode
*/
function setEmergencyMode(bool _statusEmergency) external onlyOwner {
function setEmergencyMode(bool _statusEmergency) external onlyRole(ADMIN_ROLE) {
// Check if the new emergency status is different from the current emergency status
// This prevents unnecessary updates which could lead to gas wastage
require(_statusEmergency != emergencyMode,
Expand Down
56 changes: 50 additions & 6 deletions contracts/core/stablecoin/FeeManager.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

// Importing the necessary libraries
import "@openzeppelin/contracts/access/Ownable.sol"; // Importing the Ownable contract from OpenZeppelin
// Import the AccessController interface to manage access control
import "../../interfaces/IAccessController.sol";

/**
* @title FeeManager
* @dev This contract manages the fees for a stablecoin system.
*/
contract FeeManager is Ownable {
contract FeeManager {
// State variables of the contract
uint256 public stabilityFee; // Fee charged on minting (in basis points)
uint256 public redemptionFee; // Fee charged on redemption (in basis points)
address public stablecoinCore; // Address of the stablecoin core contract

// Access control interface
IAccessController public accessController; // Access controller instance

// Constants
uint256 private constant BASIS_POINTS = 10000; // Basis points constant for calculations
Expand All @@ -31,11 +35,47 @@ contract FeeManager is Ownable {
event RedemptionFeeUpdated(uint256 newRedemptionFee);

// Constructor to initialize the FeeManager contract
constructor() Ownable(msg.sender) {
constructor(address _accessController) {
stabilityFee = 50; // Initial stability fee set to 0.5%
redemptionFee = 30; // Initial redemption fee set to 0.3%

// Set the access controller instance
accessController = IAccessController(_accessController);
}

/**
* @dev sets the address of the stablecoin core contract
* This function is only used once during deployment to avoid circular dependencies
* @param _stablecoinCore The address of the stablecoin core contract
*/
function setStablecoinCore(address _stablecoinCore) external {
// Only allow this to be set once
require(stablecoinCore == address(0), "StablecoinCore already set");
// Check if the caller is authorized to set the stablecoin core address
// This is a security measure to ensure that only authorized addresses can set the core address
require(accessController.canSetParams(msg.sender), "Not authorized");
// Check if the new stablecoin core address is valid
require(_stablecoinCore != address(0), "Invalid address");
stablecoinCore = _stablecoinCore;
}

/**
* @dev Updates the address of the access controller
* @param _newAccessController The address of the new access controller
*/
function setAccessController(address _newAccessController) external {
// Only StablecoinCore OR admin (before StablecoinCore is set) can update
require(
msg.sender == stablecoinCore ||
(stablecoinCore == address(0) && accessController.canSetParams(msg.sender)),
"Not authorized"
);
// Check if the new access controller address is valid
require(_newAccessController != address(0), "Invalid access controller address");
accessController = IAccessController(_newAccessController);
}


/**
* @dev Calculate the mint fee (Stability Fee) based on the amount
* @param amount The amount to be minted
Expand All @@ -62,7 +102,9 @@ contract FeeManager is Ownable {
* @dev Update the stability fee
* @param _newStabilityFee New stability fee (in basis points)
*/
function updateStabilityFee(uint256 _newStabilityFee) external onlyOwner {
function updateStabilityFee(uint256 _newStabilityFee) external {
// Check if the caller is authorized to update the stability fee
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
// Check if the new stability fee is valid (>0) and is at most 10% (1000 basis points)
require(_newStabilityFee > 0 && _newStabilityFee <= 1000, "Stability fee must be between 0 and 10%");
// Check if the new stability fee is different from the current stability fee
Expand All @@ -77,7 +119,9 @@ contract FeeManager is Ownable {
* @dev Update the redemption fee
* @param _newRedemptionFee New redemption fee (in basis points)
*/
function updateRedemptionFee(uint256 _newRedemptionFee) external onlyOwner {
function updateRedemptionFee(uint256 _newRedemptionFee) external {
// Check if the caller is authorized to update the redemption fee
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
// Check if the new redemption fee is valid (>0) and is at most 5% (500 basis points)
require(_newRedemptionFee > 0 && _newRedemptionFee <= 500, "Redemption fee must be between 0 and 5%");
// Check if the new redemption fee is different from the current redemption fee
Expand Down
62 changes: 52 additions & 10 deletions contracts/core/stablecoin/PegMechanism.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

// Importing the necessary libraries
import "@openzeppelin/contracts/access/Ownable.sol"; // Importing the Ownable contract from OpenZeppelin
// Import the AccessController interface to manage access control
import "../../interfaces/IAccessController.sol";

// Import all the interfaces required for the stablecoin
import "../../interfaces/IOracleAggregator.sol"; // Importing the Oracle Aggregator interface
Expand All @@ -12,7 +12,7 @@ import "../../interfaces/IStabilityReserve.sol"; // Importing the Stability Rese
* @title PegMechanism
* @dev This contract manages the peg mechanism for the stablecoin.
*/
contract PegMechanism is Ownable {
contract PegMechanism {
// State variables for the PegMechanism contract
uint256 public pegTarget; // The target peg value for the stablecoin (e.g., 1e18 for $1)
uint256 public collateralRatio; // Dynamic collateral ratio (in basis points: 10000 = 100%)
Expand All @@ -22,13 +22,17 @@ contract PegMechanism is Ownable {
uint256 public lowerPegThreshold; // Lower threshold for peg adjustment
uint256 public emergencyPegThreshold; // Emergency threshold for peg
uint256 public lastAdjustmentTime; // Last time the peg was adjusted
address public stablecoinCore; // Address of the stablecoin core contract

// Constants
uint256 private constant BASIS_POINTS = 10000; // Basis points constant for calculations
uint256 private constant PRICE_PRECISION = 1e18; // Price precision constant for calculations
// Cooldown period for peg adjustments (to prevent market turbulence)
uint256 public constant ADJUSTMENT_COOLDOWN = 6 hours;

// Access control interface
IAccessController public accessController; // Access controller instance

// Events

/**
Expand Down Expand Up @@ -82,21 +86,55 @@ contract PegMechanism is Ownable {
IStabilityReserve public stabilityReserve; // Stability Reserve interface

// Constructor to initialize the PegMechanism contract
constructor(uint256 _initialPegTarget) Ownable(msg.sender){
constructor(uint256 _initialPegTarget, address _accessController) {
pegTarget = _initialPegTarget; // Set the initial peg target
collateralRatio = 10000; // Set the initial collateral ratio to 100%
minCollateralRatio = 8000; // Set the minimum collateral ratio to 80%
maxCollateralRatio = 12000; // Set the maximum collateral ratio to 120%
upperPegThreshold = 10500; // Set the upper peg threshold to 105%
lowerPegThreshold = 9500; // Set the lower peg threshold to 95%
emergencyPegThreshold = 9000; // Set the emergency peg threshold to 90%
accessController = IAccessController(_accessController); // Set the access controller instance
}

/**
* @dev sets the address of the stablecoin core contract
* This function is only used once during deployment to avoid circular dependencies
* @param _stablecoinCore The address of the stablecoin core contract
*/
function setStablecoinCore(address _stablecoinCore) external {
// Only allow this to be set once
require(stablecoinCore == address(0), "StablecoinCore already set");
// Check if the caller is authorized to set the stablecoin core address
// This is a security measure to ensure that only authorized addresses can set the core address
require(accessController.canSetParams(msg.sender), "Not authorized");
// Check if the new stablecoin core address is valid
require(_stablecoinCore != address(0), "Invalid address");
stablecoinCore = _stablecoinCore;
}

/**
* @dev Updates the address of the access controller
* @param _newAccessController The address of the new access controller
*/
function setAccessController(address _newAccessController) external {
// Only StablecoinCore OR admin (before StablecoinCore is set) can update
require(
msg.sender == stablecoinCore ||
(stablecoinCore == address(0) && accessController.canSetParams(msg.sender)),
"Not authorized"
);
// Check if the new access controller address is valid
require(_newAccessController != address(0), "Invalid access controller address");
accessController = IAccessController(_newAccessController);
}

/**
* @dev Set the oracle aggregator contract
* @param _oracleAggregator Address of the oracle aggregator contract
*/
function setOracleAggregator(address _oracleAggregator) external onlyOwner {
function setOracleAggregator(address _oracleAggregator) external {
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
require(_oracleAggregator != address(0), "Invalid oracle aggregator address"); // Check if the address is valid
oracleAggregator = IOracleAggregator(_oracleAggregator); // Set the oracle aggregator contract
emit OracleAggregatorSet(_oracleAggregator); // Emit the event
Expand All @@ -106,7 +144,8 @@ contract PegMechanism is Ownable {
* @dev Set the stability reserve contract
* @param _stabilityReserve Address of the stability reserve contract
*/
function setStabilityReserve(address _stabilityReserve) external onlyOwner {
function setStabilityReserve(address _stabilityReserve) external {
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
require(_stabilityReserve != address(0), "Invalid stability reserve address"); // Check if the address is valid
stabilityReserve = IStabilityReserve(_stabilityReserve); // Set the stability reserve contract
emit StabilityReserveSet(_stabilityReserve); // Emit the event
Expand All @@ -124,7 +163,8 @@ contract PegMechanism is Ownable {
* @dev Update the peg target value
* @param _newPegTarget New peg target value
*/
function updatePegTarget(uint256 _newPegTarget) external onlyOwner {
function updatePegTarget(uint256 _newPegTarget) external {
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
// Check if the new peg target is valid
require(_newPegTarget > 0, "Invalid peg target value - must be greater than 0");
// Check if the new peg target is different from the current peg target
Expand All @@ -138,7 +178,8 @@ contract PegMechanism is Ownable {
* @dev Update the collateral ratio
* @param _newCollateralRatio New collateral ratio (in basis points)
*/
function updateCollateralRatio(uint256 _newCollateralRatio) external onlyOwner {
function updateCollateralRatio(uint256 _newCollateralRatio) external {
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
// Check if the new collateral ratio is valid (>0) and is at least 50% (5000 basis points)
require(_newCollateralRatio > 5000, "Collateral ratio must be greater than 50%");
// Check if the new collateral ratio is different from the current collateral ratio
Expand All @@ -157,7 +198,8 @@ contract PegMechanism is Ownable {
* @param _emergencyPegThreshold New emergency peg threshold
*/
function updatePegThresholds(uint256 _upperPegThreshold, uint256 _lowerPegThreshold, uint256 _emergencyPegThreshold)
external onlyOwner {
external {
require(accessController.canSetParams(msg.sender), "Not authorized to set parameters");
// Check if the new peg thresholds are valid (>0)
require(_upperPegThreshold > 0 && _lowerPegThreshold > 0 && _emergencyPegThreshold > 0,
"Peg thresholds must be greater than 0");
Expand All @@ -178,7 +220,7 @@ contract PegMechanism is Ownable {
*/
function adjustPeg() external returns (bool) {
require(
msg.sender == address(stabilityReserve) || msg.sender == owner(),
msg.sender == address(stabilityReserve) || accessController.canSetParams(msg.sender),
"Unauthorized caller"
);
require(block.timestamp >= lastAdjustmentTime + ADJUSTMENT_COOLDOWN, "Cooldown period not met");
Expand Down
Loading
Loading