- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
247 lines
8.4 KiB
Solidity
247 lines
8.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
|
|
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
|
|
import "../vendor/openzeppelin/UUPSUpgradeable.sol";
|
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
import "../vendor/openzeppelin/ReentrancyGuardUpgradeable.sol";
|
|
import "./interfaces/IISO4217WToken.sol";
|
|
import "./libraries/ISO4217WCompliance.sol";
|
|
|
|
/**
|
|
* @title ISO4217WToken
|
|
* @notice ISO-4217 W token (e.g., USDW, EURW, GBPW) - M1 eMoney token
|
|
* @dev Represents 1:1 redeemable digital claim on fiat currency
|
|
*
|
|
* COMPLIANCE:
|
|
* - Classification: M1 eMoney
|
|
* - Legal Tender: NO
|
|
* - Synthetic / Reserve Unit: NO
|
|
* - Commodity-Backed: NO
|
|
* - Money Multiplier: m = 1.0 (hard-fixed, no fractional reserve)
|
|
* - Backing: 1:1 with fiat currency in segregated custodial accounts
|
|
* - GRU Isolation: Direct/indirect GRU conversion prohibited
|
|
*/
|
|
contract ISO4217WToken is
|
|
IISO4217WToken,
|
|
Initializable,
|
|
ERC20Upgradeable,
|
|
AccessControlUpgradeable,
|
|
UUPSUpgradeable,
|
|
ReentrancyGuardUpgradeable
|
|
{
|
|
using ISO4217WCompliance for *;
|
|
|
|
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
|
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
|
|
bytes32 public constant RESERVE_UPDATE_ROLE = keccak256("RESERVE_UPDATE_ROLE");
|
|
|
|
string private _currencyCode; // ISO-4217 code (e.g., "USD")
|
|
uint8 private _decimals; // Token decimals (typically 2 for fiat)
|
|
uint256 private _verifiedReserve; // Verified reserve balance in base currency units
|
|
address private _custodian;
|
|
address private _mintController;
|
|
address private _burnController;
|
|
address private _complianceGuard;
|
|
|
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
constructor() {
|
|
_disableInitializers();
|
|
}
|
|
|
|
/**
|
|
* @notice Initialize the ISO-4217 W token
|
|
* @param name Token name (e.g., "USDW Token")
|
|
* @param symbol Token symbol (e.g., "USDW")
|
|
* @param currencyCode_ ISO-4217 currency code (e.g., "USD")
|
|
* @param decimals_ Token decimals (typically 2 for fiat)
|
|
* @param custodian_ Custodian address
|
|
* @param mintController_ Mint controller address
|
|
* @param burnController_ Burn controller address
|
|
* @param complianceGuard_ Compliance guard address
|
|
* @param admin Admin address
|
|
*/
|
|
function initialize(
|
|
string memory name,
|
|
string memory symbol,
|
|
string memory currencyCode_,
|
|
uint8 decimals_,
|
|
address custodian_,
|
|
address mintController_,
|
|
address burnController_,
|
|
address complianceGuard_,
|
|
address admin
|
|
) external initializer {
|
|
__ERC20_init(name, symbol);
|
|
__AccessControl_init();
|
|
__UUPSUpgradeable_init();
|
|
__ReentrancyGuard_init();
|
|
|
|
// Validate ISO-4217 format
|
|
require(
|
|
ISO4217WCompliance.isValidISO4217Format(currencyCode_),
|
|
"ISO4217WToken: invalid ISO-4217 format"
|
|
);
|
|
|
|
// Validate token symbol matches <CCC>W pattern
|
|
require(
|
|
ISO4217WCompliance.validateTokenSymbol(currencyCode_, symbol),
|
|
"ISO4217WToken: token symbol must be <CCC>W"
|
|
);
|
|
|
|
// Validate GRU isolation
|
|
require(
|
|
!ISO4217WCompliance.violatesGRUIsolation(currencyCode_),
|
|
"ISO4217WToken: GRU isolation violation"
|
|
);
|
|
|
|
require(custodian_ != address(0), "ISO4217WToken: zero custodian");
|
|
require(mintController_ != address(0), "ISO4217WToken: zero mint controller");
|
|
require(burnController_ != address(0), "ISO4217WToken: zero burn controller");
|
|
require(complianceGuard_ != address(0), "ISO4217WToken: zero compliance guard");
|
|
|
|
_currencyCode = currencyCode_;
|
|
_decimals = decimals_;
|
|
_custodian = custodian_;
|
|
_mintController = mintController_;
|
|
_burnController = burnController_;
|
|
_complianceGuard = complianceGuard_;
|
|
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
_grantRole(MINTER_ROLE, mintController_);
|
|
_grantRole(BURNER_ROLE, burnController_);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the ISO-4217 currency code this token represents
|
|
* @return currencyCode 3-letter ISO-4217 code
|
|
*/
|
|
function currencyCode() external view override returns (string memory) {
|
|
return _currencyCode;
|
|
}
|
|
|
|
/**
|
|
* @notice Override totalSupply to resolve multiple inheritance conflict
|
|
* @return Total supply of tokens
|
|
*/
|
|
function totalSupply() public view override(ERC20Upgradeable, IISO4217WToken) returns (uint256) {
|
|
return super.totalSupply();
|
|
}
|
|
|
|
/**
|
|
* @notice Get verified reserve balance
|
|
* @return reserveBalance Reserve balance in base currency units
|
|
*/
|
|
function verifiedReserve() external view override returns (uint256) {
|
|
return _verifiedReserve;
|
|
}
|
|
|
|
/**
|
|
* @notice Check if reserves are sufficient
|
|
* @dev Reserve MUST be >= supply (enforcing 1:1 backing)
|
|
* @return isSufficient True if verifiedReserve >= totalSupply
|
|
*/
|
|
function isReserveSufficient() external view override returns (bool) {
|
|
return ISO4217WCompliance.isReserveSufficient(_verifiedReserve, totalSupply());
|
|
}
|
|
|
|
/**
|
|
* @notice Get custodian address
|
|
*/
|
|
function custodian() external view override returns (address) {
|
|
return _custodian;
|
|
}
|
|
|
|
/**
|
|
* @notice Get mint controller address
|
|
*/
|
|
function mintController() external view override returns (address) {
|
|
return _mintController;
|
|
}
|
|
|
|
/**
|
|
* @notice Get burn controller address
|
|
*/
|
|
function burnController() external view override returns (address) {
|
|
return _burnController;
|
|
}
|
|
|
|
/**
|
|
* @notice Get compliance guard address
|
|
*/
|
|
function complianceGuard() external view override returns (address) {
|
|
return _complianceGuard;
|
|
}
|
|
|
|
/**
|
|
* @notice Update verified reserve (oracle/attestation)
|
|
* @dev Can only be called by authorized reserve update role
|
|
* @param newReserve New reserve balance
|
|
*/
|
|
function updateVerifiedReserve(uint256 newReserve) external onlyRole(RESERVE_UPDATE_ROLE) {
|
|
uint256 currentSupply = totalSupply();
|
|
|
|
// Enforce money multiplier = 1.0
|
|
// Reserve MUST be >= supply (1:1 backing or better)
|
|
if (newReserve < currentSupply) {
|
|
emit ReserveInsufficient(newReserve, currentSupply);
|
|
// Do not revert - allow flagging for monitoring
|
|
}
|
|
|
|
_verifiedReserve = newReserve;
|
|
emit ReserveUpdated(newReserve, block.timestamp);
|
|
}
|
|
|
|
/**
|
|
* @notice Mint tokens (only by mint controller)
|
|
* @param to Recipient address
|
|
* @param amount Amount to mint
|
|
*/
|
|
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) nonReentrant {
|
|
require(to != address(0), "ISO4217WToken: zero address");
|
|
require(amount > 0, "ISO4217WToken: zero amount");
|
|
|
|
uint256 currentSupply = totalSupply();
|
|
uint256 newSupply = currentSupply + amount;
|
|
|
|
// Enforce money multiplier = 1.0
|
|
// Reserve MUST be >= new supply (1:1 backing)
|
|
require(
|
|
_verifiedReserve >= newSupply,
|
|
"ISO4217WToken: reserve insufficient - money multiplier violation"
|
|
);
|
|
|
|
_mint(to, amount);
|
|
emit Minted(to, amount, _currencyCode);
|
|
}
|
|
|
|
/**
|
|
* @notice Burn tokens (only by burn controller)
|
|
* @param from Source address
|
|
* @param amount Amount to burn
|
|
*/
|
|
function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) nonReentrant {
|
|
require(amount > 0, "ISO4217WToken: zero amount");
|
|
|
|
_burn(from, amount);
|
|
emit Burned(from, amount, _currencyCode);
|
|
}
|
|
|
|
/**
|
|
* @notice Override decimals (typically 2 for fiat currencies)
|
|
*/
|
|
function decimals() public view virtual override returns (uint8) {
|
|
return _decimals;
|
|
}
|
|
|
|
/**
|
|
* @notice Authorize upgrade (UUPS)
|
|
* @dev Only non-monetary components may be upgraded
|
|
*/
|
|
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
// In production, add checks to ensure monetary logic is immutable
|
|
// Only allow upgrades to non-monetary components
|
|
}
|
|
}
|