180 lines
7.1 KiB
Solidity
180 lines
7.1 KiB
Solidity
// 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);
|
|
}
|
|
}
|
|
|