// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Test, console} from "forge-std/Test.sol"; import "../../../../contracts/bridge/trustless/integration/BridgeReserveCoordinator.sol"; import "../../../../contracts/bridge/trustless/BridgeSwapCoordinator.sol"; import "../../../../contracts/bridge/trustless/InboxETH.sol"; import "../../../../contracts/bridge/trustless/LiquidityPoolETH.sol"; import "../../../../contracts/bridge/trustless/ChallengeManager.sol"; import "../../../../contracts/bridge/trustless/BondManager.sol"; import "../../../../contracts/reserve/ReserveSystem.sol"; import "../../../../contracts/bridge/trustless/integration/StablecoinPegManager.sol"; import "../../../../contracts/bridge/trustless/integration/CommodityPegManager.sol"; import "../../../../contracts/bridge/trustless/integration/ISOCurrencyManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) { _mint(msg.sender, 1000000 ether); } function mint(address to, uint256 amount) external { _mint(to, amount); } } contract BridgeReserveCoordinatorTest is Test { BridgeReserveCoordinator public coordinator; BridgeSwapCoordinator public bridgeSwapCoordinator; ReserveSystem public reserveSystem; StablecoinPegManager public stablecoinPegManager; CommodityPegManager public commodityPegManager; ISOCurrencyManager public isoCurrencyManager; // Existing bridge contracts InboxETH public inbox; LiquidityPoolETH public liquidityPool; ChallengeManager public challengeManager; BondManager public bondManager; // Mock tokens MockERC20 public usdt; MockERC20 public usdc; MockERC20 public weth; MockERC20 public xau; address public deployer = address(0xDE0001); address public user = address(0x1111); function setUp() public { vm.startPrank(deployer); // Deploy mock tokens usdt = new MockERC20("Tether USD", "USDT"); usdc = new MockERC20("USD Coin", "USDC"); weth = new MockERC20("Wrapped Ether", "WETH"); xau = new MockERC20("Gold", "XAU"); // Deploy ReserveSystem reserveSystem = new ReserveSystem(deployer); // Register assets in ReserveSystem (using grantRole for PRICE_FEED_ROLE) vm.stopPrank(); vm.prank(deployer); reserveSystem.grantRole(keccak256("PRICE_FEED_ROLE"), address(this)); vm.startPrank(address(this)); // Set supported assets and prices // Note: ReserveSystem doesn't have addSupportedAsset, we'll use updatePriceFeed directly // which implicitly supports the asset // Set prices in ReserveSystem (mock) // Note: updatePriceFeed requires PRICE_FEED_ROLE vm.stopPrank(); vm.startPrank(deployer); reserveSystem.grantRole(keccak256("PRICE_FEED_ROLE"), deployer); reserveSystem.updatePriceFeed(address(usdt), 1e18, block.timestamp); // $1.00 reserveSystem.updatePriceFeed(address(usdc), 1e18, block.timestamp); // $1.00 reserveSystem.updatePriceFeed(address(weth), 1e18, block.timestamp); // 1:1 with ETH reserveSystem.updatePriceFeed(address(xau), 2000e18, block.timestamp); // $2000/oz // Deploy StablecoinPegManager stablecoinPegManager = new StablecoinPegManager(address(reserveSystem)); stablecoinPegManager.registerUSDStablecoin(address(usdt)); stablecoinPegManager.registerUSDStablecoin(address(usdc)); stablecoinPegManager.registerWETH(address(weth)); // Deploy CommodityPegManager commodityPegManager = new CommodityPegManager(address(reserveSystem)); commodityPegManager.setXAUAddress(address(xau)); commodityPegManager.registerCommodity(address(xau), "XAU", 1e18); // 1:1 with itself // Deploy ISOCurrencyManager isoCurrencyManager = new ISOCurrencyManager(address(reserveSystem)); isoCurrencyManager.setXAUAddress(address(xau)); isoCurrencyManager.registerCurrency("USD", address(usdt), 2000e18); // 1 oz XAU = 2000 USD isoCurrencyManager.registerCurrency("EUR", address(0), 1800e18); // 1 oz XAU = 1800 EUR // Deploy existing bridge contracts (simplified setup) bondManager = new BondManager(11000, 1 ether); challengeManager = new ChallengeManager(address(bondManager), 30 minutes); liquidityPool = new LiquidityPoolETH(address(weth), 5, 11000); inbox = new InboxETH(address(bondManager), address(challengeManager), address(liquidityPool)); // Create a mock SwapRouter address (we won't actually use it in these tests) address mockSwapRouter = address(0x1234567890123456789012345678901234567890); bridgeSwapCoordinator = new BridgeSwapCoordinator( address(inbox), address(liquidityPool), mockSwapRouter, address(challengeManager) ); // Deploy BridgeReserveCoordinator coordinator = new BridgeReserveCoordinator( address(bridgeSwapCoordinator), address(reserveSystem), address(stablecoinPegManager), address(commodityPegManager), address(isoCurrencyManager) ); // Deposit reserves usdt.approve(address(reserveSystem), 100000 ether); reserveSystem.depositReserve(address(usdt), 100000 ether); vm.stopPrank(); } function testVerifyReserveStatus() public { uint256 bridgeAmount = 1000 ether; BridgeReserveCoordinator.ReserveStatus memory status = coordinator.getReserveStatus( address(usdt), bridgeAmount ); assertEq(status.asset, address(usdt)); assertEq(status.bridgeAmount, bridgeAmount); assertGt(status.reserveBalance, 0); assertTrue(status.isSufficient); } function testVerifyPegStatus() public { BridgeReserveCoordinator.PegStatus[] memory pegStatuses = coordinator.verifyPegStatus(); assertGt(pegStatuses.length, 0); // Check that we have stablecoin peg statuses bool foundUSDT = false; for (uint256 i = 0; i < pegStatuses.length; i++) { if (pegStatuses[i].asset == address(usdt)) { foundUSDT = true; assertEq(pegStatuses[i].targetPrice, 1e18); // $1.00 break; } } assertTrue(foundUSDT); } function testSetReserveThreshold() public { uint256 newThreshold = 11000; // 110% vm.prank(deployer); coordinator.setReserveThreshold(newThreshold); assertEq(coordinator.reserveVerificationThresholdBps(), newThreshold); } function testRevertInvalidReserveThreshold() public { uint256 invalidThreshold = 20000; // 200% > max 150% vm.prank(deployer); vm.expectRevert(BridgeReserveCoordinator.InvalidReserveThreshold.selector); coordinator.setReserveThreshold(invalidThreshold); } }