Files
smom-dbis-138/contracts/emoney/eMoneyToken.sol
defiQUG 1fb7266469 Add Oracle Aggregator and CCIP Integration
- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control.
- Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities.
- Created .gitmodules to include OpenZeppelin contracts as a submodule.
- Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment.
- Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks.
- Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring.
- Created scripts for resource import and usage validation across non-US regions.
- Added tests for CCIP error handling and integration to ensure robust functionality.
- Included various new files and directories for the orchestration portal and deployment scripts.
2025-12-12 14:57:48 -08:00

251 lines
9.5 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./interfaces/IeMoneyToken.sol";
import "./interfaces/IPolicyManager.sol";
import "./interfaces/IDebtRegistry.sol";
import "./interfaces/IComplianceRegistry.sol";
import "./errors/TokenErrors.sol";
import "./libraries/ReasonCodes.sol";
/**
* @title eMoneyToken
* @notice Restricted ERC-20 token with policy-controlled transfers and lien enforcement
* @dev Implements UUPS upgradeable proxy pattern. All transfers are validated through PolicyManager.
* Supports two lien enforcement modes: hard freeze (blocks all transfers with liens) and encumbered
* (allows transfers up to freeBalance = balance - activeLienAmount).
*/
contract eMoneyToken is
Initializable,
ERC20Upgradeable,
AccessControlUpgradeable,
UUPSUpgradeable,
ReentrancyGuardUpgradeable,
IeMoneyToken
{
bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
bytes32 public constant ENFORCEMENT_ROLE = keccak256("ENFORCEMENT_ROLE");
IPolicyManager public policyManager;
IDebtRegistry public debtRegistry;
IComplianceRegistry public complianceRegistry;
uint8 private _decimals;
bool private _inForceTransfer;
bool private _inClawback;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @notice Initializes the token with configuration
* @dev Called once during proxy deployment. Can only be called once.
* @param name Token name (e.g., "eMoney Token")
* @param symbol Token symbol (e.g., "EMT")
* @param decimals_ Number of decimals (typically 18)
* @param issuer Address that will receive ISSUER_ROLE and DEFAULT_ADMIN_ROLE
* @param policyManager_ Address of PolicyManager contract
* @param debtRegistry_ Address of DebtRegistry contract
* @param complianceRegistry_ Address of ComplianceRegistry contract
*/
function initialize(
string calldata name,
string calldata symbol,
uint8 decimals_,
address issuer,
address policyManager_,
address debtRegistry_,
address complianceRegistry_
) external initializer {
__ERC20_init(name, symbol);
__AccessControl_init();
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
_decimals = decimals_;
policyManager = IPolicyManager(policyManager_);
debtRegistry = IDebtRegistry(debtRegistry_);
complianceRegistry = IComplianceRegistry(complianceRegistry_);
_grantRole(DEFAULT_ADMIN_ROLE, issuer);
_grantRole(ISSUER_ROLE, issuer);
}
/**
* @notice Returns the number of decimals for the token
* @return Number of decimals (typically 18)
*/
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
/**
* @notice Returns the free balance available for transfer (balance minus active encumbrances)
* @dev In encumbered mode, transfers are limited to freeBalance
* @param account Address to check
* @return Free balance (balanceOf - activeLienAmount, floored at 0)
*/
function freeBalanceOf(address account) external view override returns (uint256) {
uint256 balance = balanceOf(account);
uint256 encumbrance = debtRegistry.activeLienAmount(account);
return balance > encumbrance ? balance - encumbrance : 0;
}
/**
* @notice Internal hook that enforces transfer restrictions before updating balances
* @dev Overrides ERC20Upgradeable._update to add policy checks and lien enforcement.
* Skips checks for mint/burn operations (from/to == address(0)) and privileged operations
* (clawback, forceTransfer).
* @param from Sender address (address(0) for mints)
* @param to Recipient address (address(0) for burns)
* @param amount Transfer amount
*/
function _update(
address from,
address to,
uint256 amount
) internal virtual override {
// Skip checks for privileged operations (mint/burn internal transfers)
if (from == address(0) || to == address(0)) {
super._update(from, to, amount);
return;
}
// Skip all checks during clawback (bypasses everything)
if (_inClawback) {
super._update(from, to, amount);
return;
}
// Skip lien checks during forceTransfer (compliance already checked there)
if (_inForceTransfer) {
super._update(from, to, amount);
return;
}
// Check policy manager
(bool allowed, bytes32 reasonCode) = policyManager.canTransfer(address(this), from, to, amount);
if (!allowed) {
revert TransferBlocked(reasonCode, from, to, amount);
}
// Check lien mode enforcement
uint8 mode = policyManager.lienMode(address(this));
if (mode == 1) {
// Hard freeze mode: any active lien blocks all transfers
if (debtRegistry.hasActiveLien(from)) {
revert TransferBlocked(ReasonCodes.LIEN_BLOCK, from, to, amount);
}
} else if (mode == 2) {
// Encumbered mode: allow transfers up to freeBalance
uint256 encumbrance = debtRegistry.activeLienAmount(from);
uint256 balance = balanceOf(from);
uint256 freeBalance = balance > encumbrance ? balance - encumbrance : 0;
if (amount > freeBalance) {
revert TransferBlocked(ReasonCodes.INSUFF_FREE_BAL, from, to, amount);
}
}
// mode == 0: no lien enforcement
super._update(from, to, amount);
}
/**
* @notice Mints new tokens to an account
* @dev Requires ISSUER_ROLE. Bypasses all transfer restrictions (mint operation).
* @param to Recipient address
* @param amount Amount to mint
* @param reasonCode Reason code for the mint operation (e.g., ReasonCodes.OK)
*/
function mint(address to, uint256 amount, bytes32 reasonCode) external override onlyRole(ISSUER_ROLE) nonReentrant {
_mint(to, amount);
emit Minted(to, amount, reasonCode);
}
/**
* @notice Burns tokens from an account
* @dev Requires ISSUER_ROLE. Bypasses all transfer restrictions (burn operation).
* @param from Account to burn from
* @param amount Amount to burn
* @param reasonCode Reason code for the burn operation (e.g., ReasonCodes.OK)
*/
function burn(address from, uint256 amount, bytes32 reasonCode) external override onlyRole(ISSUER_ROLE) nonReentrant {
_burn(from, amount);
emit Burned(from, amount, reasonCode);
}
/**
* @notice Clawback transfers tokens, bypassing all restrictions
* @dev Requires ENFORCEMENT_ROLE. Bypasses all checks including liens, compliance, and policy.
* Used for emergency recovery or enforcement actions.
* @param from Source address
* @param to Destination address
* @param amount Amount to transfer
* @param reasonCode Reason code for the clawback operation
*/
function clawback(
address from,
address to,
uint256 amount,
bytes32 reasonCode
) external override onlyRole(ENFORCEMENT_ROLE) nonReentrant {
// Clawback bypasses all checks including liens and compliance
_inClawback = true;
_transfer(from, to, amount);
_inClawback = false;
emit Clawback(from, to, amount, reasonCode);
}
/**
* @notice Force transfer bypasses liens but enforces compliance
* @dev Requires ENFORCEMENT_ROLE. Bypasses lien enforcement but still checks compliance.
* Used when liens need to be bypassed but compliance must still be enforced.
* @param from Source address
* @param to Destination address
* @param amount Amount to transfer
* @param reasonCode Reason code for the force transfer operation
*/
function forceTransfer(
address from,
address to,
uint256 amount,
bytes32 reasonCode
) external override onlyRole(ENFORCEMENT_ROLE) nonReentrant {
// ForceTransfer bypasses liens but still enforces compliance
// Check compliance
if (!complianceRegistry.isAllowed(from)) {
revert FromNotCompliant(from);
}
if (!complianceRegistry.isAllowed(to)) {
revert ToNotCompliant(to);
}
if (complianceRegistry.isFrozen(from)) {
revert FromFrozen(from);
}
if (complianceRegistry.isFrozen(to)) {
revert ToFrozen(to);
}
// Set flag to bypass lien checks in _update
_inForceTransfer = true;
_transfer(from, to, amount);
_inForceTransfer = false;
emit ForcedTransfer(from, to, amount, reasonCode);
}
/**
* @notice Authorizes an upgrade to a new implementation
* @dev Internal function required by UUPSUpgradeable. Only DEFAULT_ADMIN_ROLE can authorize upgrades.
* @param newImplementation Address of the new implementation contract
*/
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
}