Files
smom-dbis-138/contracts/emoney/PolicyManager.sol
defiQUG cad927816e
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m20s
CI/CD Pipeline / Security Scanning (push) Successful in 2m48s
CI/CD Pipeline / Lint and Format (push) Failing after 47s
CI/CD Pipeline / Terraform Validation (push) Failing after 28s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 30s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 44s
Validation / validate-genesis (push) Successful in 37s
Validation / validate-terraform (push) Failing after 42s
Validation / validate-kubernetes (push) Failing after 12s
Validation / validate-smart-contracts (push) Failing after 16m44s
Validation / validate-security (push) Failing after 20s
Validation / validate-documentation (push) Failing after 19s
Verify Deployment / Verify Deployment (push) Failing after 1m21s
fix(token-list): align cUSDC_V2/cUSDT_V2 logoURI with registry policy
Use dedicated cusdc_v2/cusdt_v2 SVG paths so GRU token alignment validation passes.
2026-06-30 15:31:55 -07:00

220 lines
8.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./interfaces/IPolicyManager.sol";
import "./interfaces/IComplianceRegistry.sol";
import "./interfaces/IDebtRegistry.sol";
import "./libraries/ReasonCodes.sol";
import "./errors/RegistryErrors.sol";
/**
* @title PolicyManager
* @notice Central rule engine for transfer authorization across all eMoney tokens
* @dev Consults ComplianceRegistry and DebtRegistry to make transfer decisions.
* Manages per-token configuration including pause state, bridge-only mode, and lien modes.
* Lien enforcement is performed by eMoneyToken based on the lien mode returned here.
*/
contract PolicyManager is IPolicyManager, AccessControl {
bytes32 public constant POLICY_OPERATOR_ROLE = keccak256("POLICY_OPERATOR_ROLE");
struct TokenConfig {
bool paused;
bool bridgeOnly;
address bridge;
uint8 lienMode; // 0 = off, 1 = hard freeze, 2 = encumbered
}
IComplianceRegistry public immutable complianceRegistry;
IDebtRegistry public immutable debtRegistry;
mapping(address => TokenConfig) private _tokenConfigs;
mapping(address => mapping(address => bool)) private _tokenFreezes; // token => account => frozen
/**
* @notice Initializes PolicyManager with registry addresses
* @param admin Address that will receive DEFAULT_ADMIN_ROLE
* @param compliance Address of ComplianceRegistry contract
* @param debt Address of DebtRegistry contract
*/
constructor(address admin, address compliance, address debt) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
complianceRegistry = IComplianceRegistry(compliance);
debtRegistry = IDebtRegistry(debt);
}
/**
* @notice Returns whether a token is paused
* @param token Token address to check
* @return true if token is paused, false otherwise
*/
function isPaused(address token) external view override returns (bool) {
return _tokenConfigs[token].paused;
}
/**
* @notice Returns whether a token is in bridge-only mode
* @param token Token address to check
* @return true if token only allows transfers to/from bridge, false otherwise
*/
function bridgeOnly(address token) external view override returns (bool) {
return _tokenConfigs[token].bridgeOnly;
}
/**
* @notice Returns the bridge address for a token
* @param token Token address to check
* @return Bridge address (zero address if not set)
*/
function bridge(address token) external view override returns (address) {
return _tokenConfigs[token].bridge;
}
/**
* @notice Returns the lien mode for a token
* @param token Token address to check
* @return Lien mode: 0 = off, 1 = hard freeze, 2 = encumbered
*/
function lienMode(address token) external view override returns (uint8) {
return _tokenConfigs[token].lienMode;
}
/**
* @notice Returns whether an account is frozen for a specific token
* @param token Token address to check
* @param account Address to check
* @return true if account is frozen for this token, false otherwise
*/
function isTokenFrozen(address token, address account) external view override returns (bool) {
return _tokenFreezes[token][account];
}
/**
* @notice Determines if a transfer should be allowed
* @dev Checks in order: paused, token freezes, compliance freezes, compliance allowed status, bridge-only mode.
* Lien checks are performed by eMoneyToken based on lien mode.
* @param token Token address
* @param from Sender address
* @param to Recipient address
* @param amount Transfer amount (unused but required for interface)
* @return allowed true if transfer should be allowed, false otherwise
* @return reasonCode bytes32 reason code (ReasonCodes.OK if allowed, otherwise the blocking reason)
*/
function canTransfer(
address token,
address from,
address to,
uint256 amount
) external view override returns (bool allowed, bytes32 reasonCode) {
TokenConfig memory config = _tokenConfigs[token];
// Check paused
if (config.paused) {
return (false, ReasonCodes.PAUSED);
}
// Check token-specific freezes
if (_tokenFreezes[token][from]) {
return (false, ReasonCodes.FROM_FROZEN);
}
if (_tokenFreezes[token][to]) {
return (false, ReasonCodes.TO_FROZEN);
}
// Check compliance registry freezes
if (complianceRegistry.isFrozen(from)) {
return (false, ReasonCodes.FROM_FROZEN);
}
if (complianceRegistry.isFrozen(to)) {
return (false, ReasonCodes.TO_FROZEN);
}
// Check compliance allowed status
if (!complianceRegistry.isAllowed(from)) {
return (false, ReasonCodes.FROM_NOT_COMPLIANT);
}
if (!complianceRegistry.isAllowed(to)) {
return (false, ReasonCodes.TO_NOT_COMPLIANT);
}
// Check bridgeOnly mode
if (config.bridgeOnly) {
if (from != config.bridge && to != config.bridge) {
return (false, ReasonCodes.BRIDGE_ONLY);
}
}
// Lien mode checks are handled in eMoneyToken._update
// PolicyManager only provides the lien mode, not the enforcement
return (true, ReasonCodes.OK);
}
/**
* @notice Sets the paused state for a token
* @dev Requires POLICY_OPERATOR_ROLE. When paused, all transfers are blocked.
* @param token Token address to configure
* @param paused true to pause, false to unpause
*/
function setPaused(address token, bool paused) external override onlyRole(POLICY_OPERATOR_ROLE) {
_tokenConfigs[token].paused = paused;
emit TokenPaused(token, paused);
}
/**
* @notice Sets bridge-only mode for a token
* @dev Requires POLICY_OPERATOR_ROLE. When enabled, only transfers to/from the bridge address are allowed.
* @param token Token address to configure
* @param enabled true to enable bridge-only mode, false to disable
*/
function setBridgeOnly(address token, bool enabled) external override onlyRole(POLICY_OPERATOR_ROLE) {
_tokenConfigs[token].bridgeOnly = enabled;
emit BridgeOnlySet(token, enabled);
}
/**
* @notice Sets the bridge address for a token
* @dev Requires POLICY_OPERATOR_ROLE. Used in bridge-only mode.
* @param token Token address to configure
* @param bridgeAddr Address of the bridge contract
*/
function setBridge(address token, address bridgeAddr) external override onlyRole(POLICY_OPERATOR_ROLE) {
_tokenConfigs[token].bridge = bridgeAddr;
emit BridgeSet(token, bridgeAddr);
}
/**
* @notice Sets the lien mode for a token
* @dev Requires POLICY_OPERATOR_ROLE. Valid modes: 0 = off, 1 = hard freeze, 2 = encumbered.
* @param token Token address to configure
* @param mode Lien mode (0, 1, or 2)
*/
function setLienMode(address token, uint8 mode) external override onlyRole(POLICY_OPERATOR_ROLE) {
if (mode > 2) revert PolicyInvalidLienMode(mode);
TokenConfig storage config = _tokenConfigs[token];
bool isNewToken = config.lienMode == 0 && mode != 0 && !config.paused && !config.bridgeOnly && config.bridge == address(0);
config.lienMode = mode;
if (isNewToken) {
emit TokenConfigured(token, config.paused, config.bridgeOnly, config.bridge, mode);
}
emit LienModeSet(token, mode);
}
/**
* @notice Freezes or unfreezes an account for a specific token
* @dev Requires POLICY_OPERATOR_ROLE. Per-token freeze (in addition to global compliance freezes).
* @param token Token address
* @param account Address to freeze/unfreeze
* @param frozen true to freeze, false to unfreeze
*/
function freeze(address token, address account, bool frozen) external override onlyRole(POLICY_OPERATOR_ROLE) {
_tokenFreezes[token][account] = frozen;
emit TokenFreeze(token, account, frozen);
}
}