- Resolve stash: merge load_deployment_env path with secure-secrets and CR/LF RPC strip - create-pmm-full-mesh-chain138.sh delegates to sync-chain138-pmm-pools-from-json.sh - env.additions.example: canonical PMM pool defaults (cUSDT/USDT per crosscheck) - Include Chain138 scripts, official mirror deploy scaffolding, and prior staged changes Made-with: Cursor
103 lines
3.4 KiB
Solidity
103 lines
3.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
import "@openzeppelin/contracts/utils/Pausable.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "../compliance/LegallyCompliantBase.sol";
|
|
|
|
/**
|
|
* @title CompliantFiatToken
|
|
* @notice Generic ISO-4217 compliant fiat/commodity token (ERC-20, DEX-ready)
|
|
* @dev Use for cEURC, cGBPC, cAUDC, cJPYC, cCHFC, cCADC, cXAUC, cXAUT, etc.
|
|
* Full ERC-20 for DEX liquidity pools; inherits LegallyCompliantBase.
|
|
*
|
|
* XAU (gold) invariant — enforced by policy and integrators, not by ERC-20 math:
|
|
* When `currencyCode()` is `"XAU"` (e.g. cXAUC, cXAUT), **one full token** means
|
|
* **exactly one troy ounce** of fine gold: balances and `mint`/`transfer` amounts use
|
|
* `10 ** decimals()` base units per troy ounce (with 6 decimals, `1_000_000` base = 1 oz).
|
|
* Fiat codes (USD, EUR, …) use one full token as one unit of that currency at `decimals`.
|
|
*/
|
|
contract CompliantFiatToken is ERC20, Pausable, Ownable, LegallyCompliantBase {
|
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
|
|
|
uint8 private immutable _decimalsStorage;
|
|
string private _currencyCode;
|
|
|
|
/**
|
|
* @notice Constructor
|
|
* @param name_ Token name (e.g. "Euro Coin (Compliant)")
|
|
* @param symbol_ Token symbol (e.g. "cEURC")
|
|
* @param decimals_ Token decimals (e.g. 6)
|
|
* @param currencyCode_ ISO-4217 code (e.g. "EUR")
|
|
* @param initialOwner Owner address
|
|
* @param admin Compliance admin (DEFAULT_ADMIN_ROLE)
|
|
* @param initialSupply Initial supply to mint to deployer
|
|
*/
|
|
constructor(
|
|
string memory name_,
|
|
string memory symbol_,
|
|
uint8 decimals_,
|
|
string memory currencyCode_,
|
|
address initialOwner,
|
|
address admin,
|
|
uint256 initialSupply
|
|
)
|
|
ERC20(name_, symbol_)
|
|
Ownable(initialOwner)
|
|
LegallyCompliantBase(admin)
|
|
{
|
|
_decimalsStorage = decimals_;
|
|
_currencyCode = currencyCode_;
|
|
_grantRole(MINTER_ROLE, initialOwner);
|
|
if (initialSupply > 0) {
|
|
_mint(msg.sender, initialSupply);
|
|
}
|
|
}
|
|
|
|
function decimals() public view override returns (uint8) {
|
|
return _decimalsStorage;
|
|
}
|
|
|
|
function currencyCode() external view returns (string memory) {
|
|
return _currencyCode;
|
|
}
|
|
|
|
/**
|
|
* @notice Internal transfer with compliance tracking
|
|
*/
|
|
function _update(
|
|
address from,
|
|
address to,
|
|
uint256 amount
|
|
) internal override whenNotPaused {
|
|
super._update(from, to, amount);
|
|
if (from != address(0) && to != address(0)) {
|
|
bytes32 legalRefHash = _generateLegalReferenceHash(
|
|
from,
|
|
to,
|
|
amount,
|
|
abi.encodePacked(symbol(), " Transfer")
|
|
);
|
|
emit ValueTransferDeclared(from, to, amount, legalRefHash);
|
|
}
|
|
}
|
|
|
|
function pause() public onlyOwner {
|
|
_pause();
|
|
}
|
|
|
|
function unpause() public onlyOwner {
|
|
_unpause();
|
|
}
|
|
|
|
/// @notice Mint (DBIS Rail: grant MINTER_ROLE only to DBIS_GRU_MintController; revoke from others)
|
|
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
|
|
_mint(to, amount);
|
|
}
|
|
|
|
function burn(uint256 amount) public {
|
|
_burn(msg.sender, amount);
|
|
}
|
|
}
|