// 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_); } }