Files
smom-dbis-138/contracts/iso4217w/controllers/MintController.sol
2026-03-02 12:14:09 -08:00

144 lines
5.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../interfaces/IMintController.sol";
import "../interfaces/IISO4217WToken.sol";
import {IReserveOracle} from "../interfaces/IReserveOracle.sol";
import {IComplianceGuard} from "../interfaces/IComplianceGuard.sol";
/**
* @title MintController
* @notice Controls minting of ISO-4217 W tokens with reserve verification
* @dev Minting requires: verified fiat settlement, custodian attestation, oracle quorum
*/
contract MintController is IMintController, AccessControl, ReentrancyGuard {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
IReserveOracle public reserveOracle;
IComplianceGuard public complianceGuard;
mapping(address => bool) public isApprovedToken;
constructor(address admin, address reserveOracle_, address complianceGuard_) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
reserveOracle = IReserveOracle(reserveOracle_);
complianceGuard = IComplianceGuard(complianceGuard_);
}
/**
* @notice Mint tokens (requires reserve verification)
* @param token Token address
* @param to Recipient address
* @param amount Amount to mint (in token decimals)
* @param settlementId Fiat settlement ID for audit trail
*/
function mint(
address token,
address to,
uint256 amount,
bytes32 settlementId
) external override nonReentrant onlyRole(MINTER_ROLE) {
require(isApprovedToken[token], "MintController: token not approved");
require(amount > 0, "MintController: zero amount");
require(to != address(0), "MintController: zero address");
IISO4217WToken tokenContract = IISO4217WToken(token);
string memory currencyCode = tokenContract.currencyCode();
// Check if minting is allowed
(bool allowed, bytes32 reasonCode) = this.canMint(token, amount);
require(allowed, string(abi.encodePacked("MintController: mint not allowed: ", reasonCode)));
// Mint tokens
tokenContract.mint(to, amount);
emit MintExecuted(token, to, amount, settlementId);
}
/**
* @notice Check if minting is allowed
* @param token Token address
* @param amount Amount to mint
* @return allowed True if minting is allowed
* @return reasonCode Reason if not allowed
*/
function canMint(address token, uint256 amount) external view override returns (bool allowed, bytes32 reasonCode) {
require(isApprovedToken[token], "MintController: token not approved");
IISO4217WToken tokenContract = IISO4217WToken(token);
string memory currencyCode = tokenContract.currencyCode();
// Check oracle quorum
if (!this.isOracleQuorumMet(token)) {
return (false, keccak256("ORACLE_QUORUM_NOT_MET"));
}
// Get verified reserve
(uint256 verifiedReserve, ) = reserveOracle.getVerifiedReserve(currencyCode);
uint256 currentSupply = tokenContract.totalSupply();
// Validate mint with compliance guard
(bool isValid, bytes32 complianceReason) = complianceGuard.validateMint(
currencyCode,
amount,
currentSupply,
verifiedReserve
);
if (!isValid) {
return (false, complianceReason);
}
return (true, bytes32(0));
}
/**
* @notice Check if oracle quorum is met
* @param token Token address
* @return quorumMet True if quorum is met
*/
function isOracleQuorumMet(address token) external view override returns (bool quorumMet) {
IISO4217WToken tokenContract = IISO4217WToken(token);
string memory currencyCode = tokenContract.currencyCode();
(quorumMet, ) = reserveOracle.isQuorumMet(currencyCode);
}
/**
* @notice Approve a token for minting
* @param token Token address
*/
function approveToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
isApprovedToken[token] = true;
}
/**
* @notice Revoke token approval
* @param token Token address
*/
function revokeToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
isApprovedToken[token] = false;
}
/**
* @notice Set reserve oracle address
* @param reserveOracle_ New oracle address
*/
function setReserveOracle(address reserveOracle_) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(reserveOracle_ != address(0), "MintController: zero address");
reserveOracle = IReserveOracle(reserveOracle_);
}
/**
* @notice Set compliance guard address
* @param complianceGuard_ New guard address
*/
function setComplianceGuard(address complianceGuard_) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(complianceGuard_ != address(0), "MintController: zero address");
complianceGuard = IComplianceGuard(complianceGuard_);
}
}