// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../contracts/registry/UniversalAssetRegistry.sol"; import "../../contracts/registry/GRUAssetRegistryFacet.sol"; import "../../contracts/tokens/CompliantUSDT.sol"; import "../../contracts/tokens/CompliantUSDC.sol"; import "../../contracts/tokens/CompliantFiatToken.sol"; import "../../contracts/tokens/CompliantBTC.sol"; import "../../contracts/bridge/GRUCCIPBridge.sol"; import "../../contracts/bridge/UniversalCCIPBridge.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; /** * @title GRU Compliant Tokens Registry Integration Tests * @notice Ensures all c* tokens are integrated with GRU ERC-2535 facet path: UniversalAssetRegistry (AssetType.GRU) and GRUAssetRegistryFacet. */ contract GRUCompliantTokensRegistryTest is Test { UniversalAssetRegistry public registry; GRUAssetRegistryFacet public facet; CompliantUSDT public cusdt; CompliantUSDC public cusdc; CompliantFiatToken public ceurc; CompliantBTC public cbtc; address public admin; function setUp() public { admin = makeAddr("admin"); vm.startPrank(admin); UniversalAssetRegistry impl = new UniversalAssetRegistry(); bytes memory initData = abi.encodeCall(UniversalAssetRegistry.initialize, (admin)); ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData); registry = UniversalAssetRegistry(address(proxy)); cusdt = new CompliantUSDT(admin, admin); cusdc = new CompliantUSDC(admin, admin); ceurc = new CompliantFiatToken( "Euro Coin (Compliant)", "cEURC", 6, "EUR", admin, admin, 1_000_000 * 10**6 ); cbtc = new CompliantBTC(admin, admin, 21_000_000 * 10**8); facet = new GRUAssetRegistryFacet(); facet.setRegistry(address(registry)); vm.stopPrank(); } function test_registerGRUCompliantAsset_cUSDT() public { vm.prank(admin); registry.registerGRUCompliantAsset( address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International" ); assertTrue(registry.isAssetActive(address(cusdt))); assertEq(uint256(registry.getAssetType(address(cusdt))), uint256(UniversalAssetRegistry.AssetType.GRU)); UniversalAssetRegistry.UniversalAsset memory asset = registry.getAsset(address(cusdt)); assertEq(asset.symbol, "cUSDT"); assertEq(asset.decimals, 6); } function test_registerGRUCompliantAsset_allThree() public { vm.startPrank(admin); registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International"); registry.registerGRUCompliantAsset(address(cusdc), "USD Coin (Compliant)", "cUSDC", 6, "International"); registry.registerGRUCompliantAsset(address(ceurc), "Euro Coin (Compliant)", "cEURC", 6, "International"); vm.stopPrank(); assertTrue(registry.isAssetActive(address(cusdt))); assertTrue(registry.isAssetActive(address(cusdc))); assertTrue(registry.isAssetActive(address(ceurc))); assertEq(uint256(registry.getAssetType(address(cusdt))), uint256(UniversalAssetRegistry.AssetType.GRU)); assertEq(uint256(registry.getAssetType(address(cusdc))), uint256(UniversalAssetRegistry.AssetType.GRU)); assertEq(uint256(registry.getAssetType(address(ceurc))), uint256(UniversalAssetRegistry.AssetType.GRU)); address[] memory gruAssets = registry.getAssetsByType(UniversalAssetRegistry.AssetType.GRU); assertEq(gruAssets.length, 3); } function test_facet_delegates_to_registry() public { vm.prank(admin); registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International"); assertTrue(facet.isAssetActive(address(cusdt))); assertEq(uint256(facet.getAssetType(address(cusdt))), uint256(UniversalAssetRegistry.AssetType.GRU)); UniversalAssetRegistry.UniversalAsset memory viaFacet = facet.getAsset(address(cusdt)); assertEq(viaFacet.symbol, "cUSDT"); } function test_registerGRUMonetaryUnitAsset_cBTC() public { vm.prank(admin); registry.registerGRUCompliantAsset(address(cbtc), "Bitcoin (Compliant)", "cBTC", 8, "International"); assertTrue(registry.isAssetActive(address(cbtc))); assertEq(uint256(registry.getAssetType(address(cbtc))), uint256(UniversalAssetRegistry.AssetType.GRU)); UniversalAssetRegistry.UniversalAsset memory asset = registry.getAsset(address(cbtc)); assertEq(asset.symbol, "cBTC"); assertEq(asset.decimals, 8); } function test_GRUCCIPBridge_accepts_registered_GRU_asset() public { address mockRouter = makeAddr("router"); vm.startPrank(admin); registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International"); GRUCCIPBridge bridgeImpl = new GRUCCIPBridge(); bytes memory bridgeInit = abi.encodeCall(UniversalCCIPBridge.initialize, (address(registry), mockRouter, admin)); ERC1967Proxy bridgeProxy = new ERC1967Proxy(address(bridgeImpl), bridgeInit); GRUCCIPBridge bridge = GRUCCIPBridge(payable(address(bridgeProxy))); vm.stopPrank(); UniversalAssetRegistry.UniversalAsset memory asset = registry.getAsset(address(cusdt)); assertEq(uint256(asset.assetType), uint256(UniversalAssetRegistry.AssetType.GRU), "cUSDT must be GRU for bridge"); } function test_revert_registerGRUCompliantAsset_notRegistrar() public { vm.prank(makeAddr("stranger")); vm.expectRevert(); registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International"); } function test_revert_registerGRUCompliantAsset_alreadyRegistered() public { vm.startPrank(admin); registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International"); vm.expectRevert("Already registered"); registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International"); vm.stopPrank(); } }