// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /** * @title HolyGrail USD (HGUSD) * @notice Canonical Institutional Payment Stablecoin Implementation * @dev Architecture compliant with Basel BCBS Group 1b, US GENIUS Act, EU MiCA EMT, and ISO 20022 mapping */ abstract contract Initializable { uint8 private _initialized; bool private _initializing; event Initialized(uint8 version); modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } } library Address { function isContract(address account) internal view returns (bool) { return account.code.length > 0; } } contract HolyGrailUSD is Initializable { string public name; string public symbol; uint8 public decimals; uint256 public totalSupply; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; // Privileged Roles address public owner; address public pendingOwner; address public masterMinter; address public blacklister; address public pauser; address public rescuer; mapping(address => bool) public minters; mapping(address => uint256) public minterAllowed; bool public paused; mapping(address => bool) public isBlacklisted; // EIP-2612 Permit bytes32 public DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; // ISO 24165 Digital Token Identifier Hash bytes32 public constant DTI_IDENTIFIER = 0x8X9ZZ10120000000000000000000000000000000000000000000000000000000; // Events event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); event MasterMinterChanged(address indexed newMasterMinter); event MinterConfigured(address indexed minter, uint256 minterAllowedAmount); event MinterRemoved(address indexed minter); event Mint(address indexed minter, address indexed to, uint256 amount); event Burn(address indexed burner, uint256 amount); event Pause(); event Unpause(); event Blacklisted(address indexed _account); event UnBlacklisted(address indexed _account); event BlacklistedAccountWiped(address indexed _account, uint256 _wipedBalance); modifier onlyOwner() { require(msg.sender == owner, "Ownable: caller is not the owner"); _; } modifier onlyMasterMinter() { require(msg.sender == masterMinter, "Caller is not master minter"); _; } modifier whenNotPaused() { require(!paused, "Pausable: token transfers are paused"); _; } modifier notBlacklisted(address account) { require(!isBlacklisted[account], "Compliance: address is blacklisted"); _; } function initialize( string memory _name, string memory _symbol, uint8 _decimals, address _owner, address _masterMinter, address _blacklister, address _pauser, address _rescuer ) external initializer { name = _name; symbol = _symbol; decimals = _decimals; owner = _owner; masterMinter = _masterMinter; blacklister = _blacklister; pauser = _pauser; rescuer = _rescuer; DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(_name)), keccak256(bytes("1")), block.chainid, address(this) ) ); } function balanceOf(address account) external view returns (uint256) { return _balances[account]; } function allowance(address _owner, address _spender) external view returns (uint256) { return _allowances[_owner][_spender]; } function transfer(address to, uint256 amount) external whenNotPaused notBlacklisted(msg.sender) notBlacklisted(to) returns (bool) { _transfer(msg.sender, to, amount); return true; } function approve(address spender, uint256 amount) external whenNotPaused notBlacklisted(msg.sender) notBlacklisted(spender) returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) external whenNotPaused notBlacklisted(msg.sender) notBlacklisted(from) notBlacklisted(to) returns (bool) { uint256 currentAllowance = _allowances[from][msg.sender]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(from, msg.sender, currentAllowance - amount); } _transfer(from, to, amount); return true; } function _transfer(address from, address to, uint256 amount) internal { require(from != address(0), "ERC20: transfer from zero address"); require(to != address(0), "ERC20: transfer to zero address"); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; _balances[to] += amount; } emit Transfer(from, to, amount); } function _approve(address _owner, address _spender, uint256 amount) internal { require(_owner != address(0), "ERC20: approve from zero address"); require(_spender != address(0), "ERC20: approve to zero address"); _allowances[_owner][_spender] = amount; emit Approval(_owner, _spender, amount); } function configureMinter(address minter, uint256 minterAllowedAmount) external onlyMasterMinter returns (bool) { minters[minter] = true; minterAllowed[minter] = minterAllowedAmount; emit MinterConfigured(minter, minterAllowedAmount); return true; } function removeMinter(address minter) external onlyMasterMinter returns (bool) { minters[minter] = false; minterAllowed[minter] = 0; emit MinterRemoved(minter); return true; } function mint(address to, uint256 amount) external whenNotPaused notBlacklisted(to) returns (bool) { require(minters[msg.sender], "Caller is not an authorized minter"); require(amount <= minterAllowed[msg.sender], "Amount exceeds minter quota"); minterAllowed[msg.sender] -= amount; totalSupply += amount; _balances[to] += amount; emit Mint(msg.sender, to, amount); emit Transfer(address(0), to, amount); return true; } function burn(uint256 amount) external whenNotPaused { uint256 balance = _balances[msg.sender]; require(balance >= amount, "ERC20: burn amount exceeds balance"); _balances[msg.sender] = balance - amount; totalSupply -= amount; emit Burn(msg.sender, amount); emit Transfer(msg.sender, address(0), amount); } function permit( address _owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external whenNotPaused notBlacklisted(_owner) notBlacklisted(spender) { require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); bytes32 structHash = keccak256( abi.encode(PERMIT_TYPEHASH, _owner, spender, value, nonces[_owner]++, deadline) ); bytes32 hash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash)); address signer = ecrecover(hash, v, r, s); require(signer != address(0) && signer == _owner, "ERC20Permit: invalid signature"); _approve(_owner, spender, value); } function blacklist(address account) external { require(msg.sender == blacklister, "Caller is not blacklister"); isBlacklisted[account] = true; emit Blacklisted(account); } function unBlacklist(address account) external { require(msg.sender == blacklister, "Caller is not blacklister"); isBlacklisted[account] = false; emit UnBlacklisted(account); } function wipeBlacklistedAccount(address account) external { require(msg.sender == blacklister, "Caller is not blacklister"); require(isBlacklisted[account], "Account is not blacklisted"); uint256 dirtyFunds = _balances[account]; _balances[account] = 0; totalSupply -= dirtyFunds; emit BlacklistedAccountWiped(account, dirtyFunds); emit Transfer(account, address(0), dirtyFunds); } function pause() external { require(msg.sender == pauser, "Caller is not pauser"); paused = true; emit Pause(); } function unpause() external { require(msg.sender == pauser, "Caller is not pauser"); paused = false; emit Unpause(); } }