144 lines
5.1 KiB
Solidity
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_);
|
|
}
|
|
}
|