// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../contracts/rwa/RWAToken.sol"; import "../../contracts/rwa/RWATokenRegistry.sol"; import "../../contracts/rwa/RWATokenFactory.sol"; import "../../contracts/rwa/IRWATokenFactory.sol"; import "../../contracts/rwa/IRWAToken.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "../../contracts/rwa/RWATokenInterfaces.sol"; /// @dev Minimal UAR stub so rwa-scoped `forge test` does not pull the full upgradeable registry tree. contract MockUniversalAssetRegistry { bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE"); mapping(address => bool) private _active; function grantRole(bytes32, address) external {} function registerRWAIndexAsset( address tokenAddress, string calldata, string calldata, uint8, string calldata ) external { _active[tokenAddress] = true; } function isAssetActive(address token) external view returns (bool) { return _active[token]; } } contract RWATokenFactoryTest is Test { bytes32 internal constant METHODOLOGY_HASH = 0x6b6e599d0ba31d048b49302e263a12f0c59502f67a35100ad5c65b503b1d4b82; RWATokenRegistry public registry; RWATokenFactory public factory; MockUniversalAssetRegistry public uar; address public admin = address(0xA11CE); address public owner = address(0xB0B); address public publisher = address(0xC0DE); function setUp() public { vm.startPrank(admin); uar = new MockUniversalAssetRegistry(); registry = new RWATokenRegistry(admin); factory = new RWATokenFactory(admin, address(registry), address(uar)); registry.grantRole(registry.REGISTRAR_ROLE(), address(factory)); uar.grantRole(uar.REGISTRAR_ROLE(), address(factory)); factory.grantRole(factory.DEPLOYER_ROLE(), admin); vm.stopPrank(); } function test_deployLiXAU_registersInRegistryAndUAR() public { vm.prank(admin); address token = factory.deployRWAIndex( IRWATokenFactory.RWAProductConfig({ indexTicker: "LiXAU", name: "XAU Liquidity Index (M00)", symbol: "LiXAU", decimals: 6, assetClass: "Commodities", assetGroup: "Precious Metals", instrumentType: "Commodity Index", underlyingAsset: "Gold", gruLayer: "M00", jurisdiction: "International", initialOwner: owner, complianceAdmin: admin, indexPublisher: publisher, initialIndexValue: 1_050_000, initialSupply: 0, methodologyDocumentHash: METHODOLOGY_HASH, registerInUniversalAssetRegistry: true }) ); assertEq(IRWAToken(token).methodologyDocumentHash(), METHODOLOGY_HASH); assertTrue(registry.isRegistered("LiXAU")); assertEq(registry.getToken("LiXAU"), token); IRWAToken rwa = IRWAToken(token); assertTrue(rwa.isRwaIndex()); assertFalse(rwa.isEmoney()); assertEq(rwa.indexValue(), 1_050_000); assertEq(keccak256(bytes(rwa.indexTicker())), keccak256("LiXAU")); assertTrue(uar.isAssetActive(token)); } function test_revert_duplicateTicker() public { IRWATokenFactory.RWAProductConfig memory cfg = _liXauConfig(); vm.prank(admin); factory.deployRWAIndex(cfg); vm.prank(admin); vm.expectRevert("RWATokenFactory: already registered"); factory.deployRWAIndex(cfg); } function test_revert_invalidTicker() public { IRWATokenFactory.RWAProductConfig memory cfg = _liXauConfig(); cfg.indexTicker = "cUSDT"; vm.prank(admin); vm.expectRevert(); factory.deployRWAIndex(cfg); } function test_indexPublisherUpdatesValue() public { vm.prank(admin); address token = factory.deployRWAIndex(_liXauConfig()); vm.prank(publisher); IRWAToken(token).updateIndexValue(2_000_000); assertEq(IRWAToken(token).indexValue(), 2_000_000); } function test_eip165_and_eip712_domain() public { vm.prank(admin); address token = factory.deployRWAIndex(_liXauConfig()); IRWAToken165 rwa = IRWAToken165(token); assertTrue(rwa.supportsInterface(type(IRWAToken).interfaceId)); assertTrue(rwa.supportsInterface(type(IERC20).interfaceId)); assertTrue(rwa.supportsInterface(type(IERC20Metadata).interfaceId)); assertEq(rwa.eip20Version(), "1.0.0"); assertTrue(rwa.eip712DomainSeparator() != bytes32(0)); bytes32 h = rwa.hashIndexValueAttestation("LiXAU", 1_100_000, 1, block.timestamp + 3600); assertTrue(h != bytes32(0)); } function test_revert_zeroMethodologyHash() public { IRWATokenFactory.RWAProductConfig memory cfg = _liXauConfig(); cfg.methodologyDocumentHash = bytes32(0); vm.prank(admin); vm.expectRevert("RWATokenFactory: methodology"); factory.deployRWAIndex(cfg); } function _liXauConfig() internal view returns (IRWATokenFactory.RWAProductConfig memory) { return IRWATokenFactory.RWAProductConfig({ indexTicker: "LiXAU", name: "XAU Liquidity Index (M00)", symbol: "LiXAU", decimals: 6, assetClass: "Commodities", assetGroup: "Precious Metals", instrumentType: "Commodity Index", underlyingAsset: "Gold", gruLayer: "M00", jurisdiction: "International", initialOwner: owner, complianceAdmin: admin, indexPublisher: publisher, initialIndexValue: 1_000_000, initialSupply: 0, methodologyDocumentHash: METHODOLOGY_HASH, registerInUniversalAssetRegistry: false }); } }