141 lines
4.4 KiB
Solidity
141 lines
4.4 KiB
Solidity
// 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);
|
|
}
|
|
}
|