Files
smom-dbis-138/contracts/tokens/CompliantFiatTokenV2.sol

752 lines
26 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/Nonces.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "../compliance/LegallyCompliantBaseV2.sol";
import "./interfaces/ICompliantFiatTokenV2.sol";
/**
* @title CompliantFiatTokenV2
* @notice Canonical GRU c* V2 money token with permit, authorization transfers, explicit audit attribution,
* role-gated mint/burn, and version-aware asset identity.
*/
contract CompliantFiatTokenV2 is ERC20, Pausable, Nonces, EIP712, LegallyCompliantBaseV2, ICompliantFiatTokenV2 {
using ECDSA for bytes32;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant BRIDGE_ROLE = keccak256("BRIDGE_ROLE");
bytes32 public constant SUPPLY_ADMIN_ROLE = keccak256("SUPPLY_ADMIN_ROLE");
bytes32 public constant METADATA_ADMIN_ROLE = keccak256("METADATA_ADMIN_ROLE");
bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
bytes32 public constant JURISDICTION_ADMIN_ROLE = keccak256("JURISDICTION_ADMIN_ROLE");
bytes32 public constant REGULATOR_ROLE = keccak256("REGULATOR_ROLE");
bytes32 public constant SUPERVISOR_ROLE = keccak256("SUPERVISOR_ROLE");
bytes32 public constant EMERGENCY_ADMIN_ROLE = keccak256("EMERGENCY_ADMIN_ROLE");
bytes32 public constant OPERATION_TRANSFER = keccak256("TRANSFER");
bytes32 public constant OPERATION_MINT = keccak256("MINT");
bytes32 public constant OPERATION_BURN = keccak256("BURN");
bytes32 public constant OPERATION_TRANSFER_WITH_AUTHORIZATION = keccak256("TRANSFER_WITH_AUTHORIZATION");
bytes32 public constant OPERATION_RECEIVE_WITH_AUTHORIZATION = keccak256("RECEIVE_WITH_AUTHORIZATION");
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
keccak256(
"TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
);
bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH =
keccak256(
"ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
);
bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH =
keccak256("CancelAuthorization(address authorizer,bytes32 nonce)");
uint8 private immutable _decimalsStorage;
string private _currencyCode;
string private _tokenURI;
string public versionTag;
string public symbolDisplay;
bytes32 public immutable assetId;
bytes32 public immutable assetVersionId;
bool public forwardCanonical;
address private _owner;
address public governanceController;
bytes32 public governanceProfileId;
bytes32 public supervisionProfileId;
bytes32 public storageNamespace;
string public primaryJurisdiction;
string private _regulatoryDisclosureURI;
string private _reportingURI;
address public canonicalUnderlyingAsset;
bool public supervisionRequired;
bool public governmentApprovalRequired;
uint256 public minimumUpgradeNoticePeriod;
uint256 public supplyCap;
uint256 public mintingPeriodCap;
uint256 public mintingPeriodDuration;
uint256 public currentMintingPeriodStart;
uint256 public mintedInCurrentPeriod;
string[] private _legacyAliases;
mapping(bytes32 => bool) private _legacyAliasExists;
mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
struct OperationContext {
bool active;
bytes32 operationType;
address initiator;
address authorizer;
address executor;
bytes32 reasonHash;
bytes32 accountingRef;
bytes32 messageCorrelationId;
bytes32 additionalDataHash;
}
OperationContext private _operationContext;
error ERC2612ExpiredSignature(uint256 deadline);
error ERC2612InvalidSigner(address signer, address owner);
error AuthorizationExpired(uint256 validBefore);
error AuthorizationNotYetValid(uint256 validAfter);
error AuthorizationAlreadyUsed(address authorizer, bytes32 nonce);
error AuthorizationInvalidSigner(address signer, address authorizer);
error AuthorizationMustBeUsedByPayee(address payee, address caller);
error SupplyCapExceeded(uint256 cap, uint256 attemptedTotalSupply);
error MintCapExceeded(uint256 cap, uint256 attemptedPeriodMint);
error EmptyAlias();
error DuplicateAlias(string aliasValue);
error OwnerUnauthorized(address caller);
error ZeroOwner();
error GovernanceControllerOnly(address caller);
error GovernanceControllerNotConfigured();
error ZeroGovernanceController();
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
modifier onlyGovernanceExecution() {
if (governanceController == address(0)) revert GovernanceControllerNotConfigured();
if (_msgSender() != governanceController) revert GovernanceControllerOnly(_msgSender());
_;
}
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_,
string memory currencyCode_,
string memory versionTag_,
address initialOperator,
address admin,
uint256 initialSupply,
bool forwardCanonical_
) ERC20(name_, symbol_) EIP712(name_, versionTag_) LegallyCompliantBaseV2(admin) {
_decimalsStorage = decimals_;
_currencyCode = currencyCode_;
versionTag = versionTag_;
symbolDisplay = symbol_;
forwardCanonical = forwardCanonical_;
_owner = admin;
assetId = keccak256(bytes(string.concat("GRU:", symbol_)));
assetVersionId = keccak256(bytes(string.concat("GRU:", symbol_, ":", versionTag_)));
_grantRole(MINTER_ROLE, initialOperator);
_grantRole(BURNER_ROLE, initialOperator);
_grantRole(PAUSER_ROLE, initialOperator);
_grantRole(BRIDGE_ROLE, initialOperator);
_grantRole(SUPPLY_ADMIN_ROLE, admin);
_grantRole(METADATA_ADMIN_ROLE, admin);
_grantRole(GOVERNANCE_ROLE, admin);
_grantRole(JURISDICTION_ADMIN_ROLE, admin);
_grantRole(REGULATOR_ROLE, admin);
_grantRole(SUPERVISOR_ROLE, admin);
_grantRole(EMERGENCY_ADMIN_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
_grantRole(BURNER_ROLE, admin);
_grantRole(PAUSER_ROLE, admin);
_grantRole(BRIDGE_ROLE, admin);
governanceProfileId = keccak256(bytes(string.concat("GRU:GOV:", symbol_, ":", versionTag_)));
supervisionProfileId = keccak256(bytes(string.concat("GRU:SUP:", currencyCode_)));
storageNamespace = keccak256(bytes(string.concat("gru.storage.asset.", symbol_, ".", versionTag_)));
primaryJurisdiction = LEGAL_JURISDICTION;
supervisionRequired = true;
minimumUpgradeNoticePeriod = 7 days;
emit OwnershipTransferred(address(0), admin);
if (initialSupply > 0) {
_setOperationContext(
OPERATION_MINT,
initialOperator,
initialOperator,
initialOperator,
bytes32(0),
bytes32(0),
bytes32(0),
bytes32(0)
);
_mint(initialOperator, initialSupply);
}
}
function decimals() public view override(ERC20, IERC20Metadata) returns (uint8) {
return _decimalsStorage;
}
function currencyCode() external view returns (string memory) {
return _currencyCode;
}
function owner() public view returns (address) {
return _owner;
}
function tokenURI() external view returns (string memory) {
return _tokenURI;
}
function regulatoryDisclosureURI() external view returns (string memory) {
return _regulatoryDisclosureURI;
}
function reportingURI() external view returns (string memory) {
return _reportingURI;
}
function wrappedTransport() external pure returns (bool) {
return false;
}
function legacyAliases() external view returns (string[] memory) {
return _legacyAliases;
}
function authorizationState(address authorizer, bytes32 nonce) public view returns (bool) {
return _authorizationStates[authorizer][nonce];
}
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparatorV4();
}
function nonces(address tokenOwner) public view override(Nonces, IERC20Permit) returns (uint256) {
return super.nonces(tokenOwner);
}
function pause() external {
_checkPauseAuthority();
_pause();
}
function unpause() external {
_checkPauseAuthority();
_unpause();
}
function transferOwnership(address newOwner) external {
if (_msgSender() != _owner && !hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {
revert OwnerUnauthorized(_msgSender());
}
if (newOwner == address(0)) revert ZeroOwner();
address previousOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(previousOwner, newOwner);
}
function setGovernanceController(address governanceController_) external onlyRole(EMERGENCY_ADMIN_ROLE) {
if (governanceController_ == address(0)) revert ZeroGovernanceController();
governanceController = governanceController_;
}
function setForwardCanonical(bool value) external onlyGovernanceExecution {
_setForwardCanonicalValue(value);
}
function emergencySetForwardCanonical(bool value) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setForwardCanonicalValue(value);
}
function setTokenURI(string calldata tokenURI_) external onlyGovernanceExecution {
_setTokenURIValue(tokenURI_);
}
function emergencySetPresentationMetadata(
bool forwardCanonical_,
string calldata tokenURI_,
string calldata symbolDisplay_
) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setForwardCanonicalValue(forwardCanonical_);
_setTokenURIValue(tokenURI_);
_setSymbolDisplayValue(symbolDisplay_);
}
function setSymbolDisplay(string calldata symbolDisplay_) external onlyGovernanceExecution {
_setSymbolDisplayValue(symbolDisplay_);
}
function addLegacyAlias(string calldata aliasValue) external onlyGovernanceExecution {
_addLegacyAliasValue(aliasValue);
}
function emergencyAddLegacyAlias(string calldata aliasValue) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_addLegacyAliasValue(aliasValue);
}
function setSupplyControls(
uint256 supplyCap_,
uint256 mintingPeriodCap_,
uint256 mintingPeriodDuration_
) external onlyRole(SUPPLY_ADMIN_ROLE) {
supplyCap = supplyCap_;
mintingPeriodCap = mintingPeriodCap_;
mintingPeriodDuration = mintingPeriodDuration_;
if (mintingPeriodDuration_ > 0 && currentMintingPeriodStart == 0) {
currentMintingPeriodStart = block.timestamp;
}
emit SupplyControlsUpdated(supplyCap_, mintingPeriodCap_, mintingPeriodDuration_);
}
function setGovernanceProfileId(bytes32 governanceProfileId_) external onlyGovernanceExecution {
_setGovernanceProfileIdValue(governanceProfileId_);
}
function setSupervisionProfileId(bytes32 supervisionProfileId_) external onlyGovernanceExecution {
_setSupervisionProfileIdValue(supervisionProfileId_);
}
function setStorageNamespace(bytes32 storageNamespace_) external onlyGovernanceExecution {
_setStorageNamespaceValue(storageNamespace_);
}
function setPrimaryJurisdiction(string calldata jurisdiction_) external onlyGovernanceExecution {
_setPrimaryJurisdictionValue(jurisdiction_);
}
function setRegulatoryDisclosureURI(string calldata disclosureURI_) external onlyGovernanceExecution {
_setRegulatoryDisclosureURIValue(disclosureURI_);
}
function setReportingURI(string calldata reportingURI_) external onlyGovernanceExecution {
_setReportingURIValue(reportingURI_);
}
function setCanonicalUnderlyingAsset(address canonicalUnderlyingAsset_) external onlyGovernanceExecution {
_setCanonicalUnderlyingAssetValue(canonicalUnderlyingAsset_);
}
function setSupervisionConfiguration(
bool supervisionRequired_,
bool governmentApprovalRequired_,
uint256 minimumUpgradeNoticePeriod_
) external onlyGovernanceExecution {
_setSupervisionConfigurationValue(
supervisionRequired_,
governmentApprovalRequired_,
minimumUpgradeNoticePeriod_
);
}
function emergencySetGovernanceMetadata(
bytes32 governanceProfileId_,
bytes32 supervisionProfileId_,
bytes32 storageNamespace_,
string calldata jurisdiction_,
address canonicalUnderlyingAsset_,
bool supervisionRequired_,
bool governmentApprovalRequired_,
uint256 minimumUpgradeNoticePeriod_
) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setGovernanceProfileIdValue(governanceProfileId_);
_setSupervisionProfileIdValue(supervisionProfileId_);
_setStorageNamespaceValue(storageNamespace_);
_setPrimaryJurisdictionValue(jurisdiction_);
_setCanonicalUnderlyingAssetValue(canonicalUnderlyingAsset_);
_setSupervisionConfigurationValue(
supervisionRequired_,
governmentApprovalRequired_,
minimumUpgradeNoticePeriod_
);
}
function emergencySetDisclosureMetadata(
string calldata disclosureURI_,
string calldata reportingURI_
) external onlyRole(EMERGENCY_ADMIN_ROLE) {
_setRegulatoryDisclosureURIValue(disclosureURI_);
_setReportingURIValue(reportingURI_);
}
function recordRegulatoryApproval(
bytes32 approvalId,
string calldata actionType,
bytes32 referenceHash
) external onlyRole(REGULATOR_ROLE) {
emit RegulatoryApprovalRecorded(approvalId, actionType, referenceHash);
}
function recordSupervisoryNotice(
bytes32 noticeId,
string calldata category,
string calldata uri
) external onlyRole(SUPERVISOR_ROLE) {
emit SupervisoryNoticeRecorded(noticeId, category, uri);
}
function permit(
address tokenOwner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public {
if (block.timestamp > deadline) revert ERC2612ExpiredSignature(deadline);
bytes32 structHash = keccak256(
abi.encode(PERMIT_TYPEHASH, tokenOwner, spender, value, _useNonce(tokenOwner), deadline)
);
bytes32 digest = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(digest, v, r, s);
if (signer != tokenOwner) revert ERC2612InvalidSigner(signer, tokenOwner);
_approve(tokenOwner, spender, value);
}
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
mint(to, amount, bytes32(0));
}
function mint(address to, uint256 amount, bytes32 reasonHash) public onlyRole(MINTER_ROLE) {
_consumeMintCapacity(amount);
_setOperationContext(
OPERATION_MINT,
_msgSender(),
_msgSender(),
_msgSender(),
reasonHash,
bytes32(0),
bytes32(0),
bytes32(0)
);
_mint(to, amount);
}
function burn(uint256 amount) external {
_setOperationContext(
OPERATION_BURN,
_msgSender(),
_msgSender(),
_msgSender(),
bytes32(0),
bytes32(0),
bytes32(0),
bytes32(0)
);
_burn(_msgSender(), amount);
}
function burn(address from, uint256 amount, bytes32 reasonHash) public onlyRole(BURNER_ROLE) {
_setOperationContext(
OPERATION_BURN,
from,
from,
_msgSender(),
reasonHash,
bytes32(0),
bytes32(0),
bytes32(0)
);
_burn(from, amount);
}
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external {
_useAuthorization(
from,
to,
value,
validAfter,
validBefore,
nonce,
v,
r,
s,
TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
OPERATION_TRANSFER_WITH_AUTHORIZATION
);
}
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external {
if (_msgSender() != to) revert AuthorizationMustBeUsedByPayee(to, _msgSender());
_useAuthorization(
from,
to,
value,
validAfter,
validBefore,
nonce,
v,
r,
s,
RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
OPERATION_RECEIVE_WITH_AUTHORIZATION
);
}
function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external {
if (_authorizationStates[authorizer][nonce]) revert AuthorizationAlreadyUsed(authorizer, nonce);
bytes32 structHash = keccak256(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce));
bytes32 digest = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(digest, v, r, s);
if (signer != authorizer) revert AuthorizationInvalidSigner(signer, authorizer);
_authorizationStates[authorizer][nonce] = true;
emit AuthorizationCanceled(authorizer, nonce);
}
function _update(address from, address to, uint256 amount) internal override whenNotPaused {
OperationContext memory ctx = _operationContext;
if (!ctx.active) {
ctx = OperationContext({
active: false,
operationType: OPERATION_TRANSFER,
initiator: from == address(0) ? _msgSender() : from,
authorizer: from == address(0) ? _msgSender() : from,
executor: _msgSender(),
reasonHash: bytes32(0),
accountingRef: bytes32(0),
messageCorrelationId: bytes32(0),
additionalDataHash: bytes32(0)
});
}
_enforceCompliantOperation(
ctx.operationType,
ctx.initiator,
ctx.authorizer,
ctx.executor,
from,
to,
amount,
ctx.reasonHash,
ctx.accountingRef,
ctx.messageCorrelationId,
ctx.additionalDataHash
);
super._update(from, to, amount);
_recordCompliantOperation(
ctx.operationType,
ctx.initiator,
ctx.authorizer,
ctx.executor,
from,
to,
amount,
ctx.reasonHash,
ctx.accountingRef,
ctx.messageCorrelationId,
ctx.additionalDataHash
);
if (_operationContext.active) {
delete _operationContext;
}
}
function _useAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s,
bytes32 typeHash,
bytes32 operationType
) internal {
if (block.timestamp <= validAfter) revert AuthorizationNotYetValid(validAfter);
if (block.timestamp >= validBefore) revert AuthorizationExpired(validBefore);
if (_authorizationStates[from][nonce]) revert AuthorizationAlreadyUsed(from, nonce);
bytes32 structHash = keccak256(
abi.encode(typeHash, from, to, value, validAfter, validBefore, nonce)
);
bytes32 digest = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(digest, v, r, s);
if (signer != from) revert AuthorizationInvalidSigner(signer, from);
_authorizationStates[from][nonce] = true;
_setOperationContext(
operationType,
from,
from,
_msgSender(),
bytes32(0),
bytes32(0),
bytes32(0),
nonce
);
_transfer(from, to, value);
emit AuthorizationUsed(from, to, nonce, value);
}
function _consumeMintCapacity(uint256 amount) internal {
if (supplyCap > 0 && totalSupply() + amount > supplyCap) {
revert SupplyCapExceeded(supplyCap, totalSupply() + amount);
}
if (mintingPeriodCap == 0 || mintingPeriodDuration == 0) {
return;
}
if (currentMintingPeriodStart == 0 || block.timestamp >= currentMintingPeriodStart + mintingPeriodDuration) {
currentMintingPeriodStart = block.timestamp;
mintedInCurrentPeriod = 0;
}
uint256 attemptedPeriodMint = mintedInCurrentPeriod + amount;
if (attemptedPeriodMint > mintingPeriodCap) {
revert MintCapExceeded(mintingPeriodCap, attemptedPeriodMint);
}
mintedInCurrentPeriod = attemptedPeriodMint;
}
function _setOperationContext(
bytes32 operationType,
address initiator,
address authorizer,
address executor,
bytes32 reasonHash,
bytes32 accountingRef,
bytes32 messageCorrelationId,
bytes32 additionalDataHash
) internal {
_operationContext = OperationContext({
active: true,
operationType: operationType,
initiator: initiator,
authorizer: authorizer,
executor: executor,
reasonHash: reasonHash,
accountingRef: accountingRef,
messageCorrelationId: messageCorrelationId,
additionalDataHash: additionalDataHash
});
}
function _enforceCompliantOperation(
bytes32,
address,
address,
address,
address,
address,
uint256,
bytes32,
bytes32,
bytes32,
bytes32
) internal view virtual {
// Stub hook for future PolicyRouter / ComplianceGate / ReserveGate integration.
}
function _checkPauseAuthority() internal view {
if (_msgSender() == _owner) {
return;
}
if (!hasRole(PAUSER_ROLE, _msgSender())) {
revert OwnerUnauthorized(_msgSender());
}
}
function _setForwardCanonicalValue(bool value) internal {
forwardCanonical = value;
emit ForwardCanonicalUpdated(value);
}
function _setTokenURIValue(string memory tokenURI_) internal {
_tokenURI = tokenURI_;
emit TokenURIUpdated(tokenURI_);
}
function _setSymbolDisplayValue(string memory symbolDisplay_) internal {
symbolDisplay = symbolDisplay_;
emit SymbolDisplayUpdated(symbolDisplay_);
}
function _addLegacyAliasValue(string memory aliasValue) internal {
if (bytes(aliasValue).length == 0) revert EmptyAlias();
bytes32 aliasHash = keccak256(bytes(aliasValue));
if (_legacyAliasExists[aliasHash]) revert DuplicateAlias(aliasValue);
_legacyAliasExists[aliasHash] = true;
_legacyAliases.push(aliasValue);
emit LegacyAliasAdded(aliasValue);
}
function _setGovernanceProfileIdValue(bytes32 governanceProfileId_) internal {
governanceProfileId = governanceProfileId_;
emit GovernanceProfileUpdated(governanceProfileId_);
}
function _setSupervisionProfileIdValue(bytes32 supervisionProfileId_) internal {
supervisionProfileId = supervisionProfileId_;
emit SupervisionProfileUpdated(supervisionProfileId_);
}
function _setStorageNamespaceValue(bytes32 storageNamespace_) internal {
storageNamespace = storageNamespace_;
emit StorageNamespaceUpdated(storageNamespace_);
}
function _setPrimaryJurisdictionValue(string memory jurisdiction_) internal {
primaryJurisdiction = jurisdiction_;
emit PrimaryJurisdictionUpdated(jurisdiction_);
}
function _setRegulatoryDisclosureURIValue(string memory disclosureURI_) internal {
_regulatoryDisclosureURI = disclosureURI_;
emit RegulatoryDisclosureURIUpdated(disclosureURI_);
}
function _setReportingURIValue(string memory reportingURI_) internal {
_reportingURI = reportingURI_;
emit ReportingURIUpdated(reportingURI_);
}
function _setCanonicalUnderlyingAssetValue(address canonicalUnderlyingAsset_) internal {
canonicalUnderlyingAsset = canonicalUnderlyingAsset_;
emit CanonicalUnderlyingAssetUpdated(canonicalUnderlyingAsset_);
}
function _setSupervisionConfigurationValue(
bool supervisionRequired_,
bool governmentApprovalRequired_,
uint256 minimumUpgradeNoticePeriod_
) internal {
supervisionRequired = supervisionRequired_;
governmentApprovalRequired = governmentApprovalRequired_;
minimumUpgradeNoticePeriod = minimumUpgradeNoticePeriod_;
emit SupervisionConfigurationUpdated(
supervisionRequired_,
governmentApprovalRequired_,
minimumUpgradeNoticePeriod_
);
}
}