// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {CWNavOracle} from "../../contracts/cw-settlement/CWNavOracle.sol"; import {CWRedemptionQueue} from "../../contracts/cw-settlement/CWRedemptionQueue.sol"; import {CWStabilityFund} from "../../contracts/cw-settlement/CWStabilityFund.sol"; contract MockNavBridge { mapping(address => uint256) internal _locked; mapping(address => uint256) internal _outstanding; function setLocked(address token, uint256 amount) external { _locked[token] = amount; } function lockedBalance(address token) external view returns (uint256) { return _locked[token]; } function totalOutstanding(address token) external view returns (uint256) { return _outstanding[token]; } } contract MockReserveSystem { mapping(address => uint256) internal _balances; function setBalance(address asset, uint256 amount) external { _balances[asset] = amount; } function getReserveBalance(address asset) external view returns (uint256) { return _balances[asset]; } } contract MockERC20Six is ERC20 { constructor() ERC20("Mock", "MOCK") {} function mint(address to, uint256 amount) external { _mint(to, amount); } function decimals() public pure override returns (uint8) { return 6; } } contract CWNavOracleTest is Test { MockNavBridge internal bridge; MockReserveSystem internal reserveSystem; CWNavOracle internal navOracle; MockERC20Six internal canonical; function setUp() public { bridge = new MockNavBridge(); reserveSystem = new MockReserveSystem(); navOracle = new CWNavOracle(address(this), address(bridge), address(reserveSystem)); canonical = new MockERC20Six(); navOracle.configureToken(address(canonical), address(canonical)); } function test_publishNav_computesBackingAndNavPerShare() public { bridge.setLocked(address(canonical), 120 * 1e6); reserveSystem.setBalance(address(canonical), 30 * 1e6); navOracle.publishNav( address(canonical), 100 * 1e6, 8200, 1500, 300, keccak256("attestation-v1") ); CWNavOracle.NavSnapshot memory snap = navOracle.getNavSnapshot(address(canonical)); assertEq(snap.totalLockedAssets, 120 * 1e6); assertEq(snap.totalReserveAssets, 30 * 1e6); assertEq(snap.totalCwSupply, 100 * 1e6); assertEq(snap.backingRatioBps, 15000); assertEq(snap.navPerShare, 15e17); } } contract CWRedemptionQueueTest is Test { CWRedemptionQueue internal queue; MockERC20Six internal token; function setUp() public { queue = new CWRedemptionQueue(address(this)); token = new MockERC20Six(); queue.grantRole(queue.BRIDGE_ROLE(), address(this)); } function test_instantTier_claimableImmediately() public { token.mint(address(this), 1_000 * 1e6); token.approve(address(queue), 1_000 * 1e6); uint256 requestId = queue.fundAndEnqueue(address(this), address(token), 1_000 * 1e6); queue.claim(requestId); assertEq(token.balanceOf(address(this)), 1_000 * 1e6); } function test_standardTier_requiresDelay() public { uint256 requestId = queue.enqueueRedemption(address(this), address(token), 10_000 * 1e6); vm.expectRevert(); queue.claim(requestId); vm.warp(block.timestamp + 24 hours); queue.processDueRequests(_arr(requestId)); vm.expectRevert(); queue.claim(requestId); } function _arr(uint256 id) internal pure returns (uint256[] memory ids) { ids = new uint256[](1); ids[0] = id; } } contract CWStabilityFundTest is Test { CWStabilityFund internal fund; MockERC20Six internal token; function setUp() public { fund = new CWStabilityFund(address(this)); token = new MockERC20Six(); fund.setSupportedAsset(address(token), true); } function test_depositAndEmergencyWithdraw() public { token.mint(address(this), 100e6); token.approve(address(fund), 100e6); fund.deposit(address(token), 100e6); fund.emergencyWithdraw(address(token), address(this), 50e6, keccak256("depeg")); assertEq(token.balanceOf(address(this)), 50e6); } }